
/*++

Copyright (c) 1994-2000 Microsoft Corporation

Module Name:

    mapper.c

Abstract:

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

Author:

    Bob Rinne (BobRi) 15-Oct-1994

Environment:

    Kernel mode

Revision History :

--*/

#include "pnpmgrp.h"
#pragma hdrstop

#ifdef POOL_TAGGING
#undef ExAllocatePool
#define ExAllocatePool(a,b) ExAllocatePoolWithTag(a,b,'rpaM')
#endif


//
// This contains information obtained by checking the firmware
// tree of the registry
//

typedef struct _FIRMWARE_CONFIGURATION {
    struct _FIRMWARE_CONFIGURATION *Next;
    INTERFACE_TYPE     BusType;
    ULONG              BusNumber;
    CONFIGURATION_TYPE ControllerType;
    ULONG              ControllerNumber;
    CONFIGURATION_TYPE PeripheralType;
    ULONG              PeripheralNumber;
    ULONG              NumberBases;
    ULONG              ResourceDescriptorSize;
    PVOID              ResourceDescriptor;
    ULONG              IdentifierLength;
    ULONG              IdentifierType;
    PVOID              Identifier;
    WCHAR const*       PnPId;
    BOOLEAN            NewlyCreated;
} FIRMWARE_CONFIGURATION, *PFIRMWARE_CONFIGURATION;

//
// Device extension information
//

typedef struct _DEVICE_EXTENSION {
    PDEVICE_OBJECT     DeviceObject;
    PDRIVER_OBJECT     DriverObject;
    INTERFACE_TYPE     InterfaceType;
    ULONG              BusNumber;
    PFIRMWARE_CONFIGURATION FirmwareList;
} DEVICE_EXTENSION, *PDEVICE_EXTENSION;
//
// mapping table from firmware to enum
//

typedef struct _FIRMWARE_IDENT_TO_PNP_ID {
    PWCHAR  FirmwareName;
    PWCHAR  PnPId;
} FIRMWARE_IDENT_TO_PNP_ID, *PFIRMWARE_IDENT_TO_PNP_ID;

//
// table to hold seed information for a firmware tree entry.
//

#define OPTIONS_NONE                    0x00000000
#define OPTIONS_INSERT_PNP_ID           0x00000001
#define OPTIONS_INSERT_DEVICEDESC       0x00000002
#define OPTIONS_INSERT_COMPATIBLE_IDS   0x00000004
#define OPTIONS_INSERT_PHANTOM_MARKER   0x00000008
typedef struct _MAPPER_SEED {
    PWCHAR  ValueName;
    ULONG   ValueType;
    ULONG   DwordValueContent;
    ULONG   Options;
} MAPPER_SEED, *PMAPPER_SEED;

//
// table to hold key names and attributes for construction
// in the root enumerator tree
//

#define KEY_SEED_REQUIRED               0x00000000
#define KEY_SEED_DEVICE_PARAMETERS      0x00000001
typedef struct _KEY_SEED {
    PWCHAR  KeyName;
    ULONG   Attribute;
    ULONG   Options;
} KEY_SEED, *PKEY_SEED;


//
// All the data here is INIT only
//

#ifdef ALLOC_DATA_PRAGMA
#pragma const_seg("INITCONST")
#pragma data_seg("INITDATA")
#endif

DEVICE_EXTENSION MapperDeviceExtension;

//
// This table is used to translate the firmware tree information
// to the root enumerator PNP id for keyboard devices.
//

const FIRMWARE_IDENT_TO_PNP_ID KeyboardMap[] = {
    L"XT_83KEY",        L"*PNP0300",
    L"PCAT_86KEY",      L"*PNP0301",
    L"PCXT_84KEY",      L"*PNP0302",
    L"XT_84KEY",        L"*PNP0302",
    L"101-KEY",         L"*PNP0303",
    L"OLI_83KEY",       L"*PNP0304",
    L"ATT_301",         L"*PNP0304",
    L"OLI_102KEY",      L"*PNP0305",
    L"OLI_86KEY",       L"*PNP0306",
    L"OLI_A101_102KEY", L"*PNP0309",
    L"ATT_302",         L"*PNP030a",
    L"PCAT_ENHANCED",   L"*PNP030b",
    L"PC98_106KEY",     L"*nEC1300",
    L"PC98_LaptopKEY",  L"*nEC1300",
    L"PC98_N106KEY",    L"*PNP0303",
    NULL, NULL
};

#define PS2_KEYBOARD_COMPATIBLE_ID  L"PS2_KEYBOARD"
#define PS2_MOUSE_COMPATIBLE_ID     L"PS2_MOUSE"

//
// This table is used to translate the firmware tree information
// to the root enumerator PNP id for pointer devices.
//

const FIRMWARE_IDENT_TO_PNP_ID PointerMap[] = {
    L"PS2 MOUSE",                        L"*PNP0F0E",
    L"SERIAL MOUSE",                     L"*PNP0F0C",
    L"MICROSOFT PS2 MOUSE",              L"*PNP0F03",
    L"LOGITECH PS2 MOUSE",               L"*PNP0F12",
    L"MICROSOFT INPORT MOUSE",           L"*PNP0F02",
    L"MICROSOFT SERIAL MOUSE",           L"*PNP0F01",
    L"MICROSOFT BALLPOINT SERIAL MOUSE", L"*PNP0F09",
    L"LOGITECH SERIAL MOUSE",            L"*PNP0F08",
    L"MICROSOFT BUS MOUSE",              L"*PNP0F00",
    L"NEC PC-9800 BUS MOUSE",            L"*nEC1F00",
    NULL, NULL
};

//
// the MapperValueSeed table is a NULL terminated table (i.e. the name
// pointer is NULL) that contains the list of values and their type
// for insertion in a newly created root enumerator key.
//

const MAPPER_SEED MapperValueSeed[] = {
    REGSTR_VALUE_HARDWAREID,       REG_MULTI_SZ, 0, OPTIONS_INSERT_PNP_ID,
    REGSTR_VALUE_COMPATIBLEIDS,    REG_MULTI_SZ, 0, OPTIONS_INSERT_COMPATIBLE_IDS,
    REGSTR_VAL_FIRMWAREIDENTIFIED, REG_DWORD,    1, OPTIONS_NONE,
    REGSTR_VAL_DEVDESC,            REG_SZ,       0, OPTIONS_INSERT_DEVICEDESC,
    REGSTR_VAL_PHANTOM,            REG_DWORD,    1, OPTIONS_INSERT_PHANTOM_MARKER,
    NULL, 0, 0, 0
};

//
// the MapperKeySeed table is a NULL terminated table (i.e. the name
// pointer is NULL) that contains the list of keys to and their
// attributes (volatile or non-volatile) for keys to be created under
// a newly created root enumerator key.
//
// The preceeding backslash is required on all entries in this table.
//

const KEY_SEED MapperKeySeed[] = {
    L"\\Control",           REG_OPTION_VOLATILE,     KEY_SEED_REQUIRED,
    L"\\LogConf",           REG_OPTION_NON_VOLATILE, KEY_SEED_REQUIRED,
    L"",                    REG_OPTION_NON_VOLATILE, KEY_SEED_DEVICE_PARAMETERS,
    NULL, 0, 0
};

//
// SerialId is used as the PNP id for all serial controllers.
// NOTE: there is no code to detect presense of a 16550.
//

const WCHAR SerialId[] = L"*PNP0501"; // RDR should be two entries.  *PNP0501 is 16550

//
// ParallelId is used as the PNP id for all parallel controllers.
// NOTE: there is no code to detect presense of ECP support.
//

const WCHAR ParallelId[] = L"*PNP0400"; // RDR should be two entries.  *PNP0401 is ECP

//
// FloppyId is used as the PNP id for all floppy peripherals.
//

const WCHAR FloppyId[] = L"*PNP0700";

//
// ATAId is here, but not used - there is nothing in the firmware
// tree for the IDE controller.
//

const WCHAR ATAId[] = L"*PNP0600";

//
// Proto type declarations
//

FIRMWARE_IDENT_TO_PNP_ID const*
MapperFindIdentMatch(
    FIRMWARE_IDENT_TO_PNP_ID const* IdentTable,
    PWCHAR String
    );

WCHAR const*
MapperTranslatePnPId(
    CONFIGURATION_TYPE PeripheralType,
    PKEY_VALUE_FULL_INFORMATION Identifier
    );

NTSTATUS
MapperPeripheralCallback(
    IN PVOID                        Context,
    IN PUNICODE_STRING              PathName,
    IN INTERFACE_TYPE               BusType,
    IN ULONG                        BusNumber,
    IN PKEY_VALUE_FULL_INFORMATION *BusInformation,
    IN CONFIGURATION_TYPE           ControllerType,
    IN ULONG                        ControllerNumber,
    IN PKEY_VALUE_FULL_INFORMATION *ControllerInformation,
    IN CONFIGURATION_TYPE           PeripheralType,
    IN ULONG                        PeripheralNumber,
    IN PKEY_VALUE_FULL_INFORMATION *PeripheralInformation
    );

NTSTATUS
MapperCallback(
    IN PVOID                        Context,
    IN PUNICODE_STRING              PathName,
    IN INTERFACE_TYPE               BusType,
    IN ULONG                        BusNumber,
    IN PKEY_VALUE_FULL_INFORMATION *BusInformation,
    IN CONFIGURATION_TYPE           ControllerType,
    IN ULONG                        ControllerNumber,
    IN PKEY_VALUE_FULL_INFORMATION *ControllerInformation,
    IN CONFIGURATION_TYPE           PeripheralType,
    IN ULONG                        PeripheralNumber,
    IN PKEY_VALUE_FULL_INFORMATION *PeripheralInformation
    );

VOID
MapperMarkKey(
    IN HANDLE Handle,
    IN PUNICODE_STRING  PathName,
    IN PFIRMWARE_CONFIGURATION FirmwareEntry
    );

VOID
MapperSeedKey(
    IN HANDLE                  Handle,
    IN PUNICODE_STRING         PathName,
    IN PFIRMWARE_CONFIGURATION FirmwareEntry,
    IN BOOLEAN                 DeviceIsPhantom
    );

PCM_RESOURCE_LIST
MapperAdjustResourceList (
    IN     PCM_RESOURCE_LIST ResourceList,
    IN     WCHAR const*      PnPId,
    IN OUT PULONG            Size
    );

NTSTATUS
ComPortDBAdd(
    IN  HANDLE  DeviceParamKey,
    IN  PWSTR   PortName
    );

#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT, MapperFindIdentMatch)
#pragma alloc_text(INIT, MapperTranslatePnPId)
#pragma alloc_text(INIT, MapperPeripheralCallback)
#pragma alloc_text(INIT, MapperCallback)
#pragma alloc_text(INIT, MapperProcessFirmwareTree)
#pragma alloc_text(INIT, MapperMarkKey)
#pragma alloc_text(INIT, MapperSeedKey)
#pragma alloc_text(INIT, MapperFreeList)
#pragma alloc_text(INIT, MapperConstructRootEnumTree)
#pragma alloc_text(INIT, MapperAdjustResourceList)
#pragma alloc_text(INIT, ComPortDBAdd)
#pragma alloc_text(INIT, MapperPhantomizeDetectedComPorts)
#endif

FIRMWARE_IDENT_TO_PNP_ID const*
MapperFindIdentMatch(
    FIRMWARE_IDENT_TO_PNP_ID const* IdentTable,
    PWCHAR                    String
    )

/*++

Routine Description:

    Given a table of strings to match, find the match for
    the identifier given.

Arguments:

Return Value:

    A pointer to the ident table entry for the match if found
    NULL if not found.

--*/

{
    FIRMWARE_IDENT_TO_PNP_ID const* entry;

    entry = IdentTable;
    while (entry->FirmwareName) {
        if (!wcscmp(String, entry->FirmwareName)) {
            return entry;
        }
        entry++;
    }
    return NULL;
}

WCHAR const*
MapperTranslatePnPId(
    CONFIGURATION_TYPE          PeripheralType,
    PKEY_VALUE_FULL_INFORMATION Identifier
    )

/*++

Routine Description:

    Given the peripheral type and a location in the firmware tree
    this routine will determine the PnP Id to be used when constructing
    the root enumeration portion of the registry.

Arguments:

    PeripheralType - the type of item being translated (keyboard, mouse, etc)
    PathName       - the registry path name into the firmware tree for
                     this device.

Return Value:

    A pointer to the PnP Id string if a map is found.

--*/

{
    FIRMWARE_IDENT_TO_PNP_ID const* identMap;
    PWSTR identifierString = NULL;
    WCHAR const* idStr;

    if (Identifier) {
        identifierString = (PWSTR)((PUCHAR)Identifier + Identifier->DataOffset);
        IopDbgPrint((IOP_MAPPER_INFO_LEVEL,
                    "Mapper: identifier = %ws\n\tType = ",
                    identifierString));
    }

    idStr = NULL;
    switch (PeripheralType) {
    case DiskController:
        IopDbgPrint((IOP_MAPPER_INFO_LEVEL,
                    "%s (%d)\n",
                    "DiskController",
                    PeripheralType));
        idStr = FloppyId;
        break;

    case SerialController:
        IopDbgPrint((IOP_MAPPER_INFO_LEVEL,
                    "%s (%d)\n",
                    "SerialController",
                    PeripheralType));
        idStr = SerialId;
        break;

    case ParallelController:
        IopDbgPrint((IOP_MAPPER_INFO_LEVEL,
                    "%s (%d)\n",
                    "ParallelController",
                    PeripheralType));
        idStr = ParallelId;
        break;

    case PointerController:
        IopDbgPrint((IOP_MAPPER_INFO_LEVEL,
                    "%s (%d)\n",
                    "PointerController",
                    PeripheralType));
        idStr = PointerMap[0].PnPId;
        break;

    case KeyboardController:
        IopDbgPrint((IOP_MAPPER_INFO_LEVEL,
                    "%s (%d)\n",
                    "KeyboardController",
                    PeripheralType));
        idStr = KeyboardMap[0].PnPId;
        break;

    case DiskPeripheral:
        IopDbgPrint((IOP_MAPPER_INFO_LEVEL,
                    "%s (%d)\n",
                    "DiskPeripheral",
                    PeripheralType));
        break;

    case FloppyDiskPeripheral:
        IopDbgPrint((IOP_MAPPER_INFO_LEVEL,
                    "%s (%d)\n",
                    "FloppyDiskPeripheral",
                    PeripheralType));
        idStr = FloppyId;
        break;

    case PointerPeripheral:
        if (identifierString) {
            identMap = MapperFindIdentMatch(PointerMap, identifierString);
            if (identMap) {
                IopDbgPrint((IOP_MAPPER_INFO_LEVEL,
                            "%ws\n",
                            identMap->PnPId));
                idStr = identMap->PnPId;
            } else {
                IopDbgPrint((IOP_MAPPER_ERROR_LEVEL,
                            "Mapper: No pointer match found\n"));
            }
        } else {
            IopDbgPrint((IOP_MAPPER_ERROR_LEVEL,
                        "Mapper: No identifier specified\n"));
        }
        break;

    case KeyboardPeripheral:
        if (identifierString) {
            identMap = MapperFindIdentMatch(KeyboardMap, identifierString);

            if (identMap) {
                IopDbgPrint((IOP_MAPPER_INFO_LEVEL,
                            "%ws\n",
                            identMap->PnPId));
                idStr = identMap->PnPId;
            } else {
                IopDbgPrint((IOP_MAPPER_ERROR_LEVEL,
                            "Mapper: No keyboard match found\n"));
            }
        } else {
            IopDbgPrint((IOP_MAPPER_ERROR_LEVEL,
                        "Mapper: No identifier specified\n"));
        }
        break;

    default:
        IopDbgPrint((IOP_MAPPER_ERROR_LEVEL,
                    "Mapper: Unknown device (%d)\n",
                    PeripheralType));
        break;
    }
    return idStr;
}


NTSTATUS
MapperPeripheralCallback(
    IN PVOID                        Context,
    IN PUNICODE_STRING              PathName,
    IN INTERFACE_TYPE               BusType,
    IN ULONG                        BusNumber,
    IN PKEY_VALUE_FULL_INFORMATION *BusInformation,
    IN CONFIGURATION_TYPE           ControllerType,
    IN ULONG                        ControllerNumber,
    IN PKEY_VALUE_FULL_INFORMATION *ControllerInformation,
    IN CONFIGURATION_TYPE           PeripheralType,
    IN ULONG                        PeripheralNumber,
    IN PKEY_VALUE_FULL_INFORMATION *PeripheralInformation
    )

/*++

Routine Description:

    This routine is used to acquire firmware tree information about
    pointer devices in the system.

Arguments:

    Context               - Pointer to the device extension.
    PathName              - unicode registry path.
    BusType               - Internal, Isa, ...
    BusNumber             - Which bus if we are on a multibus system.
    BusInformation        - Configuration information about the bus. Not Used.
    ControllerType        - serial or ata disk.
    ControllerNumber      - Which controller if there is more than one
                            controller in the system.
    ControllerInformation - Array of pointers to the three pieces of
                            registry information.
    PeripheralType        - Undefined for this call.
    PeripheralNumber      - Undefined for this call.
    PeripheralInformation - Undefined for this call.

Return Value:

    STATUS_SUCCESS if everything went ok, or STATUS_INSUFFICIENT_RESOURCES
    if it couldn't map the base csr or acquire the device object, or
    all of the resource information couldn't be acquired.

--*/

{
    PFIRMWARE_CONFIGURATION     firmwareEntry = Context;
    PKEY_VALUE_FULL_INFORMATION information;
    ULONG                       dataLength;
    PWCHAR                      ptr;
    PVOID                       temp;

    UNREFERENCED_PARAMETER( BusType );
    UNREFERENCED_PARAMETER( BusNumber );
    UNREFERENCED_PARAMETER( BusInformation );
    UNREFERENCED_PARAMETER( ControllerType );
    UNREFERENCED_PARAMETER( ControllerNumber );

    IopDbgPrint((IOP_MAPPER_INFO_LEVEL,
                "Mapper: peripheral registry location is\n %ws\n",
                PathName->Buffer));

    if (!ControllerInformation) {
        IopDbgPrint((IOP_MAPPER_VERBOSE_LEVEL,
                    "Mapper: No component information\n"));
    }
    if (!PeripheralInformation) {
        IopDbgPrint((IOP_MAPPER_VERBOSE_LEVEL,
                    "Mapper: No peripheral information\n"));
        return STATUS_SUCCESS;
    }

    //
    // Map the PnP Id for this device.
    //

    if (PeripheralInformation[IoQueryDeviceIdentifier]) {
        information = PeripheralInformation[IoQueryDeviceIdentifier];
        firmwareEntry->PnPId = MapperTranslatePnPId(PeripheralType, information);

        if (firmwareEntry->PnPId) {
            //
            // Remember the peripheral's identifier (if it has one, and it's a REG_SZ value)
            // for use as the default PnP device description.
            //

            if (((dataLength = information->DataLength) > sizeof(WCHAR)) &&
                (information->Type == REG_SZ)) {

                ptr = (PWCHAR) ((PUCHAR)information + information->DataOffset);

                if (*ptr) {
                    temp = ExAllocatePool(NonPagedPool, dataLength);
                    if (temp) {

                        //
                        // If there's already an identifier here (from the peripheral's
                        // controller) then wipe it out.
                        //

                        if(firmwareEntry->Identifier) {
                            ExFreePool(firmwareEntry->Identifier);
                        }

                        //
                        // Move the data
                        //

                        firmwareEntry->Identifier = temp;
                        firmwareEntry->IdentifierType = information->Type;
                        firmwareEntry->IdentifierLength = dataLength;
                        RtlCopyMemory(temp, ptr, dataLength);
                    }
                }
            }
        }
    }

    //
    // Save the ordinals for the peripheral type and number
    //

    firmwareEntry->PeripheralType = PeripheralType;
    firmwareEntry->PeripheralNumber = PeripheralNumber;

    return STATUS_SUCCESS;
}

NTSTATUS
MapperCallback(
    IN PVOID                        Context,
    IN PUNICODE_STRING              PathName,
    IN INTERFACE_TYPE               BusType,
    IN ULONG                        BusNumber,
    IN PKEY_VALUE_FULL_INFORMATION *BusInformation,
    IN CONFIGURATION_TYPE           ControllerType,
    IN ULONG                        ControllerNumber,
    IN PKEY_VALUE_FULL_INFORMATION *ControllerInformation,
    IN CONFIGURATION_TYPE           PeripheralType,
    IN ULONG                        PeripheralNumber,
    IN PKEY_VALUE_FULL_INFORMATION *PeripheralInformation
    )

/*++

Routine Description:

    This routine is used to acquire firmware tree information about
    pointer devices in the system.

Arguments:

    Context               - Pointer to the device extension.
    PathName              - unicode registry path.
    BusType               - Internal, Isa, ...
    BusNumber             - Which bus if we are on a multibus system.
    BusInformation        - Configuration information about the bus. Not Used.
    ControllerType        - serial or ata disk.
    ControllerNumber      - Which controller if there is more than one
                            controller in the system.
    ControllerInformation - Array of pointers to the three pieces of
                            registry information.
    PeripheralType        - Undefined for this call.
    PeripheralNumber      - Undefined for this call.
    PeripheralInformation - Undefined for this call.

Return Value:

    STATUS_SUCCESS if everything went ok, or STATUS_INSUFFICIENT_RESOURCES
    if it couldn't map the base csr or acquire the device object, or
    all of the resource information couldn't be acquired.

--*/

{
    PDEVICE_EXTENSION               deviceExtension = Context;
    PCM_FULL_RESOURCE_DESCRIPTOR    controllerData;
    PKEY_VALUE_FULL_INFORMATION     information;
    PFIRMWARE_CONFIGURATION         firmwareEntry;
    CONFIGURATION_TYPE              peripheralType;
    PUCHAR                          buffer;
    ULONG                           dataLength;

    UNREFERENCED_PARAMETER( BusInformation );
    UNREFERENCED_PARAMETER( PeripheralType );
    UNREFERENCED_PARAMETER( PeripheralNumber );
    UNREFERENCED_PARAMETER( PeripheralInformation );

    //
    // If entry is found, but there is no information just return
    //

    information = ControllerInformation[IoQueryDeviceConfigurationData];
    if (information == NULL) {

        return STATUS_SUCCESS;
    }

    dataLength = information->DataLength;
    if (dataLength == 0) {

        return STATUS_SUCCESS;
    }

    //
    // Setup to capture the information from the firmware tree
    //

    firmwareEntry = ExAllocatePool(NonPagedPool, sizeof(FIRMWARE_CONFIGURATION));
    if (!firmwareEntry) {

        return STATUS_INSUFFICIENT_RESOURCES;
    }
    RtlZeroMemory(firmwareEntry, sizeof(FIRMWARE_CONFIGURATION));

    //
    // Save information concerning the controller
    //

    firmwareEntry->ControllerType   = ControllerType;
    firmwareEntry->ControllerNumber = ControllerNumber;
    firmwareEntry->BusNumber = BusNumber;
    firmwareEntry->BusType   = BusType;

    //
    // Save the resource descriptor
    //

    buffer = firmwareEntry->ResourceDescriptor = ExAllocatePool(NonPagedPool,
                                                                dataLength);

    if (!buffer) {
        ExFreePool(firmwareEntry);
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    //
    // Save the configuration information on this controller.
    //

    controllerData = (PCM_FULL_RESOURCE_DESCRIPTOR)
        ((PUCHAR)information + information->DataOffset);
    RtlCopyMemory(buffer, controllerData, dataLength);
    firmwareEntry->ResourceDescriptorSize = dataLength;

    //
    // If there is a device identifier save it.
    //

    information = ControllerInformation[IoQueryDeviceIdentifier];
    if (information != NULL) {
        PWCHAR ptr;

        dataLength = information->DataLength;
        if (dataLength != 0) {

            ptr = (PWCHAR) ((PUCHAR)information + information->DataOffset);
            if (ControllerType == ParallelController) {
                PWCHAR tmpChar;

                //
                // Some extra mapping is performed here to
                // translate the firmware names to LPT names.
                //

                *ptr++ = (WCHAR) 'L';
                *ptr++ = (WCHAR) 'P';
                *ptr++ = (WCHAR) 'T';

                //
                // Find the number.
                //

                tmpChar = ptr;
                while (*tmpChar) {
                    if ((*tmpChar >= (WCHAR) '0') &&
                        (*tmpChar <= (WCHAR) '9')) {
                        break;
                    }
                    tmpChar++;
                }

                if (*tmpChar) {
                    while (*tmpChar) {
                        *ptr++ = *tmpChar++;
                    }
                    *ptr = (WCHAR) 0;

                    //
                    // Update the datalength to be 4 wchars and eos and
                    // restore the pointer.
                    //

                    ptr = (PWCHAR) ((PUCHAR)information + information->DataOffset);
                    dataLength = 10;
                } else {
                    dataLength = 0;
                    IopDbgPrint((IOP_MAPPER_ERROR_LEVEL,
                                "Mapper: no parallel port number!\n"));
                }
            }

            if (dataLength) {
                firmwareEntry->Identifier = ExAllocatePool(NonPagedPool,
                                                           dataLength);
                if (firmwareEntry->Identifier) {

                    //
                    // Move the data
                    //

                    firmwareEntry->IdentifierType = information->Type;
                    firmwareEntry->IdentifierLength = dataLength;
                    RtlCopyMemory(firmwareEntry->Identifier, ptr, dataLength);
                }
            }
        }
    }

    //
    // For some controllers, search the peripheral information
    //

    switch (ControllerType) {
    case SerialController:
    case ParallelController:
        //
        // Don't look for a peripheral.
        //
        peripheralType = (CONFIGURATION_TYPE) 0;
        break;
    case DiskController:
        peripheralType = FloppyDiskPeripheral;
        break;
    case KeyboardController:
        peripheralType = KeyboardPeripheral;
        break;
    case PointerController:
        peripheralType = PointerPeripheral;
        break;
    default:
        peripheralType = (CONFIGURATION_TYPE) 0;
        break;
    }

    IopDbgPrint((IOP_MAPPER_INFO_LEVEL,
                "Mapper: registry location is\n %ws\n",
                PathName->Buffer));

    IopDbgPrint((IOP_MAPPER_INFO_LEVEL,
                "Mapper: ControllerInformation[] -\n\tIdent: %x -\n\tData: %x -\n\tInformation: %x\n",
                ControllerInformation[0],
                ControllerInformation[1],
                ControllerInformation[2]));

    if (peripheralType) {
        IopDbgPrint((IOP_MAPPER_INFO_LEVEL,
                    "Mapper: searching for peripheral type %d\n",
                    peripheralType));

        IoQueryDeviceDescription(&BusType,
                                 &BusNumber,
                                 &ControllerType,
                                 &ControllerNumber,
                                 &peripheralType,
                                 NULL,
                                 MapperPeripheralCallback,
                                 firmwareEntry);
    }

    //
    // firmwareEntry->PnPId will be NULL if there are no peripherals of this
    // type in the tree or if the peripheral's description doesn't match one of
    // those in our table.
    //
    // firmwareEntry->PeripheralType will be equal to peripheralType if we found
    // one of the proper type regardless of whether or not it is in the table.
    //
    // So this test just ensures that we fallback to the controller IDs in the
    // case were there is no peripheral entry.  If there is a peripheral entry
    // that we don't understand we will suppress the entire node.
    //
    // This prevents creating devices with hw ids of bogus as we were seeing on
    // the SGI x86 ARC machines.
    //

    if (!firmwareEntry->PnPId && firmwareEntry->PeripheralType == 0) {

        //
        // Attempt to get PnPId from the controller type.
        //

        firmwareEntry->PnPId = MapperTranslatePnPId(ControllerType, NULL);

        if (!firmwareEntry->PnPId) {
            IopDbgPrint((IOP_MAPPER_INFO_LEVEL,
                        "Mapper: NO PnP Id for\n ==> %ws\n",
                        PathName->Buffer));
        }
    }

    IopDbgPrint((IOP_MAPPER_INFO_LEVEL,
                "Mapper: constructed name %d_%d_%d_%d_%d_%d\n",
                firmwareEntry->BusType,
                firmwareEntry->BusNumber,
                firmwareEntry->ControllerType,
                firmwareEntry->ControllerNumber,
                firmwareEntry->PeripheralType,
                firmwareEntry->PeripheralNumber));

    if (firmwareEntry->PnPId) {

        //
        // Link into chain of entries.
        //

        firmwareEntry->Next = deviceExtension->FirmwareList;
        deviceExtension->FirmwareList = firmwareEntry;
    } else {

        //
        // No map found - don't remember this entry.
        //

        ExFreePool(buffer);
        if(firmwareEntry->Identifier) {
            ExFreePool(firmwareEntry->Identifier);
        }
        ExFreePool(firmwareEntry);
    }
    return STATUS_SUCCESS;
}


VOID
MapperProcessFirmwareTree(
    IN BOOLEAN OnlyProcessSerialPorts
    )

/*++

Routine Description:

    Query the information in the firmware tree to know what
    system board devices were located.  This will cause a FirmwareList
    to be created on the device extention passed.

Arguments:

    OnlyProcessSerialPorts - if non-zero, then we'll only look at serial ports.
        This is done on ACPI machines where, in general, we don't want to pay
        attention to ntdetect/firmware information (but we have to for serial
        ports so that legacy add-in ISA serial ports and modems are detected
        automatically as in previous versions of NT as well as Win9x).

Return Value:

    None

--*/

{
    INTERFACE_TYPE     interfaceType;
    ULONG              index;
    CONFIGURATION_TYPE sc;
    CONFIGURATION_TYPE controllerTypes[] = { PointerController,
                                             KeyboardController,
                                             ParallelController,
                                             DiskController,
                                             FloppyDiskPeripheral,
                                             SerialController   // must be last
                                           };
#define CONTROLLER_TYPES_COUNT (sizeof(controllerTypes) / sizeof(controllerTypes[0]))

    PAGED_CODE();

    //
    // Locate all firmware controller information and save its resource usage.
    //
    // It's pretty inefficient to be going through all interface types, when we
    // really only care about a very small subset of non-PnP buses (e.g., ISA,
    // EISA, maybe Internal).
    //

    for (interfaceType = 0; interfaceType < MaximumInterfaceType; interfaceType++) {

        IopDbgPrint((IOP_MAPPER_VERBOSE_LEVEL,
                    "Mapper: searching on interface ===> %d\n",
                    interfaceType));

        if(OnlyProcessSerialPorts) {

            //
            // Start out at the last element of the array, so we only process
            // SerialControllers.
            //

            index = CONTROLLER_TYPES_COUNT - 1;
        } else {
            index = 0;
        }

        for ( ; index < CONTROLLER_TYPES_COUNT; index++) {
            sc = controllerTypes[index];

            IoQueryDeviceDescription(&interfaceType,
                                     NULL,
                                     &sc,
                                     NULL,
                                     NULL,
                                     NULL,
                                     MapperCallback,
                                     &MapperDeviceExtension);
        }
    }
}


VOID
MapperMarkKey(
    IN HANDLE           Handle,
    IN PUNICODE_STRING  PathName,
    IN PFIRMWARE_CONFIGURATION FirmwareEntry
    )

/*++

Routine Description:

    Record in the root enum key that the firmware mapper found this entry.
    Migrate configuration information entries.

Arguments:

    Handle   - handle to the key
    PathName - base path name to this key
    FirmwareEntry - information from the firmware tree.

Return Value:

    None

--*/

{
    OBJECT_ATTRIBUTES objectAttributes;
    PCM_RESOURCE_LIST resourceList;
    UNICODE_STRING    unicodeName;
    NTSTATUS          status;
    HANDLE            subKeyHandle;
    PWCHAR            wcptr;
    ULONG             disposition;
    ULONG             buffer;
    USHORT            originalLength;

    //
    // Mark that this entry was in the firmware tree.
    //

    buffer = 1;
    PiWstrToUnicodeString(&unicodeName, REGSTR_VAL_FIRMWAREIDENTIFIED);

    ZwSetValueKey(Handle,
                  &unicodeName,
                  0,
                  REG_DWORD,
                  &buffer,
                  sizeof(ULONG));

    //
    // Create the control subkey
    //

    IopDbgPrint((IOP_MAPPER_INFO_LEVEL,
                "Mapper: marking existing key\n"));
    originalLength = PathName->Length;
    wcptr = (PWCHAR) ((PUCHAR)PathName->Buffer + PathName->Length);
    wcptr++; // locate eos

    //
    // Build the volatile control key
    //

    InitializeObjectAttributes(&objectAttributes,
                               PathName,
                               OBJ_CASE_INSENSITIVE,
                               NULL,
                               NULL);
    RtlAppendUnicodeToString(PathName, L"\\Control");
    status = ZwCreateKey(&subKeyHandle,
                         KEY_READ | KEY_WRITE,
                         &objectAttributes,
                         0,
                         NULL,
                         REG_OPTION_VOLATILE,
                         &disposition);

    if (NT_SUCCESS(status)) {

        //
        // Create the found by firmware volatile.
        //

        buffer = 1;
        PiWstrToUnicodeString(&unicodeName, REGSTR_VAL_FIRMWAREMEMBER);

        ZwSetValueKey(subKeyHandle,
                      &unicodeName,
                      0,
                      REG_DWORD,
                      &buffer,
                      sizeof(ULONG));
        ZwClose(subKeyHandle);

    } else {

        //
        // ignore failures
        //

        IopDbgPrint((IOP_MAPPER_ERROR_LEVEL,
                    "Mapper: failed to mark control key %x\n",
                    status));
    }

    //
    // if there is a resource descriptor, restore path and open LogConf key.
    //

    if (FirmwareEntry->ResourceDescriptor) {
        PathName->Length = originalLength;
        *wcptr = (WCHAR) 0;

        InitializeObjectAttributes(&objectAttributes,
                                   PathName,
                                   OBJ_CASE_INSENSITIVE,
                                   NULL,
                                   NULL);
        RtlAppendUnicodeToString(PathName, L"\\LogConf");
        status = ZwCreateKey(&subKeyHandle,
                             KEY_READ | KEY_WRITE,
                             &objectAttributes,
                             0,
                             NULL,
                             REG_OPTION_VOLATILE,
                             &disposition);

        if (NT_SUCCESS(status)) {
            ULONG size;

            //
            // two entries need to be made:
            // BootConfig:REG_RESOURCE_LIST
            // BasicConfigVector:REG_RESOURCE_REQUIREMENTS_LIST
            //

            size = sizeof(CM_RESOURCE_LIST) -
                   sizeof(CM_FULL_RESOURCE_DESCRIPTOR) +
                   FirmwareEntry->ResourceDescriptorSize;

            resourceList = ExAllocatePool(NonPagedPool, size);

            if (resourceList) {

                resourceList->Count = 1;
                RtlCopyMemory(&resourceList->List[0],
                              FirmwareEntry->ResourceDescriptor,
                              FirmwareEntry->ResourceDescriptorSize);

                resourceList = MapperAdjustResourceList (
                                   resourceList,
                                   FirmwareEntry->PnPId,
                                   &size
                                   );

                PiWstrToUnicodeString(&unicodeName,
                                     L"BootConfig");
                ZwSetValueKey(subKeyHandle,
                              &unicodeName,
                              0,
                              REG_RESOURCE_LIST,
                              resourceList,
                              size);
#if 0
                //
                // Now do the resource requirements list.
                //

                reqList = IopCmResourcesToIoResources(0, resourceList);

                if (reqList) {
                    PiWstrToUnicodeString(&unicodeName,
                                         L"BasicConfigVector");
                    ZwSetValueKey(subKeyHandle,
                                  &unicodeName,
                                  0,
                                  REG_RESOURCE_REQUIREMENTS_LIST,
                                  reqList,
                                  reqList->ListSize);
                    ExFreePool(reqList);
                }
#endif
                ExFreePool(resourceList);
            }

            ZwClose(subKeyHandle);

        } else {

            //
            // ignore errors
            //

            IopDbgPrint((IOP_MAPPER_ERROR_LEVEL,
                        "Mapper: failed to update logconf key %x\n",
                        status));
        }
    }

    //
    // Restore path passed in.
    //

    PathName->Length = originalLength;
    *wcptr = (WCHAR) 0;
}

VOID
MapperSeedKey(
    IN HANDLE                  Handle,
    IN PUNICODE_STRING         PathName,
    IN PFIRMWARE_CONFIGURATION FirmwareEntry,
    IN BOOLEAN                 DeviceIsPhantom
    )

/*++

Routine Description:

    This routine seeds a registry key with enough information
    to get PnP to run the class installer on the devnode.

Arguments:

    Handle          - handle to the key

    PathName        - base path name to this key

    FirmwareEntry   - information from the firmware tree

    DeviceIsPhantom - if non-zero, add "Phantom" value entry so the root
        enumerator will skip this device instance (i.e., not turn it into a
        devnode)

Return Value:

    None

--*/

{
#define SEED_BUFFER_SIZE (512 * sizeof(WCHAR))
    UNICODE_STRING    unicodeName;
    OBJECT_ATTRIBUTES objectAttributes;
    MAPPER_SEED const* valueSeed;
    KEY_SEED const*   keySeed;
    NTSTATUS          status;
    HANDLE            subKeyHandle;
    WCHAR const*      pnpid;
    PWCHAR            buffer;
    PWCHAR            wcptr;
    ULONG             disposition;
    ULONG             size;
    USHORT            originalLength;

    buffer = ExAllocatePool(NonPagedPool, SEED_BUFFER_SIZE);
    if (!buffer) {
        return;
    }
    RtlZeroMemory(buffer, SEED_BUFFER_SIZE);

    //
    // Create subkeys.
    //

    originalLength = PathName->Length;
    wcptr = (PWCHAR) ((PUCHAR)PathName->Buffer + PathName->Length);

    for (keySeed = MapperKeySeed; keySeed->KeyName; keySeed++) {

        //
        // Reset the base path for the next key to seed.
        //

        *wcptr = (WCHAR) 0;
        PathName->Length = originalLength;
        RtlAppendUnicodeToString(PathName, keySeed->KeyName);

        //
        // Only build a device parameters key if there is something
        // to put in the key (i.e., this is a serial or parallel port).
        //

        if (keySeed->Options & KEY_SEED_DEVICE_PARAMETERS) {
            if (((FirmwareEntry->ControllerType != SerialController) && (FirmwareEntry->ControllerType != ParallelController)) ||
                !FirmwareEntry->Identifier) {
                continue;
            }

            status = IopOpenDeviceParametersSubkey( &subKeyHandle,
                                                    NULL,
                                                    PathName,
                                                    KEY_READ | KEY_WRITE
                                                    );
            if (NT_SUCCESS(status)) {
                status = STATUS_SUCCESS;
            } else {
                status = STATUS_UNSUCCESSFUL;
            }
        } else {

            //
            // need to construct this key.
            //

            InitializeObjectAttributes(&objectAttributes,
                                       PathName,
                                       OBJ_CASE_INSENSITIVE,
                                       NULL,
                                       NULL);
            status = ZwCreateKey(&subKeyHandle,
                                 KEY_READ | KEY_WRITE,
                                 &objectAttributes,
                                 0,
                                 NULL,
                                 keySeed->Attribute,
                                 &disposition);
        }

        if (NT_SUCCESS(status)) {

            //
            // Check to see if this is the parameters key and
            // migrate the parameter information.
            //

            if (keySeed->Options & KEY_SEED_DEVICE_PARAMETERS) {

                if (FirmwareEntry->ControllerType == SerialController)  {

                    ComPortDBAdd(subKeyHandle, (PWSTR)FirmwareEntry->Identifier);
                } else {
                    //
                    // to get here there must be identifier information
                    // in the FirmwareEntry, so that check is not performed.
                    //
                    // NOTE: this will only happen once - when the key is
                    // created -- perhaps this needs to happen on every
                    // boot.
                    //

                    PiWstrToUnicodeString(&unicodeName,
                                        L"DosDeviceName");
                    ZwSetValueKey(subKeyHandle,
                                &unicodeName,
                                0,
                                FirmwareEntry->IdentifierType,
                                FirmwareEntry->Identifier,
                                FirmwareEntry->IdentifierLength);
                }
            }
            ZwClose(subKeyHandle);
        } else {

            //
            // ignore failures
            //

            IopDbgPrint((IOP_MAPPER_ERROR_LEVEL,
                        "Mapper: failed to build control key %x\n",
                        status));
        }
    }

    //
    // Undo the mangling of the path name performed in the loop above.
    //

    *wcptr = (WCHAR) 0;
    PathName->Length = originalLength;

    //
    // Create values.
    //

    pnpid = FirmwareEntry->PnPId;
    for (valueSeed = MapperValueSeed; valueSeed->ValueName; valueSeed++) {

        if (valueSeed->ValueType == REG_DWORD) {

            if ((valueSeed->Options == OPTIONS_INSERT_PHANTOM_MARKER) &&
                !DeviceIsPhantom) {

                //
                // Device isn't a phantom--we don't want to mark it as such.
                //

                continue;
            }

            size = sizeof(ULONG);
            RtlCopyMemory(buffer, &valueSeed->DwordValueContent, size);

        } else if (valueSeed->Options == OPTIONS_INSERT_PNP_ID) {

            size = (ULONG)((wcslen(pnpid) + 2) * sizeof(WCHAR)); // eos multi_sz
            if (FirmwareEntry->BusType == Eisa) {

                //
                // need a mult_sz of EISA\PNPblah *PNPblah
                //

                RtlZeroMemory(buffer, SEED_BUFFER_SIZE);
                wcptr = (PWCHAR)pnpid;
                wcptr++;
                swprintf(buffer, L"EISA\\%s", wcptr);

                wcptr = buffer;
                while (*wcptr) {
                    wcptr++;
                }
                wcptr++; // step past eos for 1st string

                RtlCopyMemory(wcptr, pnpid, size);

                size += (ULONG)((PUCHAR)wcptr - (PUCHAR)buffer);
            } else {
                RtlCopyMemory(buffer, pnpid, size - sizeof(WCHAR));
                buffer[size / sizeof(WCHAR) - 1] = L'\0';
            }
        } else if (valueSeed->Options == OPTIONS_INSERT_COMPATIBLE_IDS) {
            if (FirmwareEntry->PeripheralType == KeyboardPeripheral)  {
                size = sizeof(PS2_KEYBOARD_COMPATIBLE_ID);
                RtlCopyMemory(buffer, PS2_KEYBOARD_COMPATIBLE_ID, size);
            } else if (FirmwareEntry->PeripheralType == PointerPeripheral &&
                       (wcscmp(pnpid, L"*PNP0F0E") == 0 ||
                        wcscmp(pnpid, L"*PNP0F03") == 0 ||
                        wcscmp(pnpid, L"*PNP0F12") == 0)) {
                size = sizeof(PS2_MOUSE_COMPATIBLE_ID);
                RtlCopyMemory(buffer, PS2_MOUSE_COMPATIBLE_ID, size);
            } else {
                continue;
            }
            buffer[size / 2] = L'\0';  // 2nd NUL for MULTI_SZ
            size += sizeof(L'\0');
        } else if (valueSeed->Options == OPTIONS_INSERT_DEVICEDESC) {
            size = FirmwareEntry->IdentifierLength;
            RtlCopyMemory(buffer, FirmwareEntry->Identifier, size);
        } else {
            IopDbgPrint((IOP_MAPPER_ERROR_LEVEL, "Mapper: NO VALUE TYPE!\n"));
            ASSERT(FALSE);
            continue;
        }

        RtlInitUnicodeString(&unicodeName,
                             valueSeed->ValueName);
        ZwSetValueKey(Handle,
                      &unicodeName,
                      0,
                      valueSeed->ValueType,
                      buffer,
                      size);
    }
    ExFreePool(buffer);
}


VOID
MapperFreeList(
    VOID
    )

/*++

Routine Description:

    This routine walks through the list of firmware entries
    and frees all allocated memory.

Arguments:

    None

Return Value:

    None

--*/

{
    PDEVICE_EXTENSION       deviceExtension = &MapperDeviceExtension;
    PFIRMWARE_CONFIGURATION tempEntry;
    PFIRMWARE_CONFIGURATION firmwareEntry;

    firmwareEntry = deviceExtension->FirmwareList;
    while (firmwareEntry) {

        //
        // free allocated structures associated with the firmware entry
        //

        if (firmwareEntry->ResourceDescriptor) {
            ExFreePool(firmwareEntry->ResourceDescriptor);
        }
        if (firmwareEntry->Identifier) {
            ExFreePool(firmwareEntry->Identifier);
        }

        //
        // free this entry and move to the next
        //

        tempEntry = firmwareEntry->Next;
        ExFreePool(firmwareEntry);
        firmwareEntry = tempEntry;
    }
}

VOID
MapperConstructRootEnumTree(
    IN BOOLEAN CreatePhantomDevices
    )

/*++

Routine Description:

    This routine walks through the list of firmware entries
    in the device extension and migrates the information into
    the root enumerator's tree in the registry.

Arguments:

    CreatePhantomDevices - If non-zero, then the device instances are created
        as "phantoms" (i.e., they are marked with the "Phantom" value entry so
        that the root enumerator will ignore them).  The only time these device
        instance registry keys will ever turn into real live devnodes is if the
        class installer (in response to DIF_FIRSTTIMESETUP or DIF_DETECT)
        decides that these devices aren't duplicates of any PnP-enumerated
        devnodes, and subsequently registers and installs them.

Return Value:

    None

--*/

{
#define ENUM_KEY_BUFFER_SIZE (1024 * sizeof(WCHAR))
#define INSTANCE_BUFFER_SIZE (256 * sizeof(WCHAR))
    UNICODE_STRING          enumKey;
    PFIRMWARE_CONFIGURATION firmwareEntry;
    OBJECT_ATTRIBUTES       objectAttributes;
    NTSTATUS                status;
    BOOLEAN                 keyPresent;
    PWCHAR                  registryBase;
    PWCHAR                  instanceBuffer;
    HANDLE                  handle;
    ULONG                   disposition;
    PVOID                   buffer;
    PDEVICE_EXTENSION       DeviceExtension = &MapperDeviceExtension;

    PAGED_CODE();

    //
    // allocate space needed for the registry path into the root
    // enumerator tree.  Note, limited size on path length.
    //

    buffer = ExAllocatePool(NonPagedPool, ENUM_KEY_BUFFER_SIZE);

    if (!buffer) {
        MapperFreeList();
        IopDbgPrint((IOP_MAPPER_ERROR_LEVEL,
                    "Mapper: could not allocate memory for registry update\n"));
        return;
    }

    instanceBuffer = ExAllocatePool(NonPagedPool, INSTANCE_BUFFER_SIZE);
    if (!instanceBuffer) {
        MapperFreeList();
        ExFreePool(buffer);
        IopDbgPrint((IOP_MAPPER_ERROR_LEVEL,
                    "Mapper: could not allocate memory for instance buffer\n"));
        return;
    }

    InitializeObjectAttributes(&objectAttributes,
                               &enumKey,
                               OBJ_CASE_INSENSITIVE,
                               NULL,
                               NULL);

#if UMODETEST
    registryBase = L"\\Registry\\Machine\\System\\TestControlSet\\Enum\\Root\\";
#else
    registryBase = L"\\Registry\\Machine\\System\\CurrentControlSet\\Enum\\Root\\";
#endif

    firmwareEntry = DeviceExtension->FirmwareList;
    while (firmwareEntry) {

        //
        // Construct the base for the path for this entry.
        //


        PiWstrToUnicodeString(&enumKey, NULL);
        enumKey.MaximumLength = ENUM_KEY_BUFFER_SIZE;
        enumKey.Buffer = buffer;
        RtlZeroMemory(buffer, ENUM_KEY_BUFFER_SIZE);
        RtlAppendUnicodeToString(&enumKey, registryBase);
        RtlAppendUnicodeToString(&enumKey, firmwareEntry->PnPId);

        //
        // Build the pnp Key.
        //

        status = ZwCreateKey(&handle,
                             KEY_READ | KEY_WRITE,
                             &objectAttributes,
                             0,
                             NULL,
                             REG_OPTION_NON_VOLATILE,
                             &disposition);

        if (NT_SUCCESS(status)) {

            //
            // Do not need the handle, so close it
            // Remember if the key was present prior to call
            //

            ZwClose(handle);
            keyPresent = (disposition == REG_OPENED_EXISTING_KEY) ? TRUE : FALSE;
            IopDbgPrint((IOP_MAPPER_INFO_LEVEL,
                        "Mapper: Key was %s\n",
                        keyPresent ? "Present" : "Created"));

            //
            // Construct the instance name.
            //

            RtlZeroMemory(instanceBuffer, INSTANCE_BUFFER_SIZE);
            swprintf(instanceBuffer,
                     L"\\%d_%d_%d_%d_%d_%d",
                     firmwareEntry->BusType,
                     firmwareEntry->BusNumber,
                     firmwareEntry->ControllerType,
                     firmwareEntry->ControllerNumber,
                     firmwareEntry->PeripheralType,
                     firmwareEntry->PeripheralNumber);
            RtlAppendUnicodeToString(&enumKey, instanceBuffer);

            status = ZwCreateKey(&handle,
                                 KEY_READ | KEY_WRITE,
                                 &objectAttributes,
                                 0,
                                 NULL,
                                 REG_OPTION_NON_VOLATILE,
                                 &disposition);

            if (NT_SUCCESS(status)) {

                if (firmwareEntry->ResourceDescriptor) {
                    IopDbgPrint((IOP_MAPPER_INFO_LEVEL,
                                "Mapper: firmware entry has resources %x\n",
                                firmwareEntry->ResourceDescriptor));
                }

                if (firmwareEntry->Identifier) {
                    IopDbgPrint((IOP_MAPPER_INFO_LEVEL,
                                "Mapper: firmware entry has identifier %x\n",
                                firmwareEntry->Identifier));
                }

                //
                // If the key already exists because it was explicitly migrated
                // during textmode setup, we should still consider it a "new key".
                //
                if (disposition != REG_CREATED_NEW_KEY) {
                    PKEY_VALUE_FULL_INFORMATION keyValueInformation;
                    UNICODE_STRING unicodeString;

                    status = IopGetRegistryValue(handle,
                                                 REGSTR_VALUE_MIGRATED,
                                                 &keyValueInformation);
                    if (NT_SUCCESS(status)) {

                        if ((keyValueInformation->Type == REG_DWORD) &&
                            (keyValueInformation->DataLength == sizeof(ULONG)) &&
                            ((*(PULONG)KEY_VALUE_DATA(keyValueInformation)) != 0)) {
                            disposition = REG_CREATED_NEW_KEY;
                        }

                        ExFreePool(keyValueInformation);

                        PiWstrToUnicodeString(&unicodeString, REGSTR_VALUE_MIGRATED);
                        ZwDeleteValueKey(handle, &unicodeString);
                    }
                }

                //
                // Only if this is a new entry do we see the key.
                //

                if (disposition == REG_CREATED_NEW_KEY) {

                    //
                    // Remember the fact that the key was newly-created for the
                    // PnP BIOS case where we need to come along and "phantomize"
                    // all newly-created ntdetect COM ports.
                    //

                    firmwareEntry->NewlyCreated = TRUE;

                    //
                    // Create enough information to get pnp to
                    // install drivers
                    //

                    MapperSeedKey(handle,
                                  &enumKey,
                                  firmwareEntry,
                                  CreatePhantomDevices
                                 );
                }
                MapperMarkKey(handle,
                              &enumKey,
                              firmwareEntry);
                ZwClose(handle);

            } else {
                IopDbgPrint((IOP_MAPPER_ERROR_LEVEL,
                            "Mapper: create of instance key failed %x\n",
                            status));
            }

        } else {
            IopDbgPrint((IOP_MAPPER_ERROR_LEVEL,
                        "Mapper: create pnp key failed %x\n",
                        status));
        }

        firmwareEntry = firmwareEntry->Next;
    }
    ExFreePool(instanceBuffer);
}

PCM_RESOURCE_LIST
MapperAdjustResourceList (
    IN     PCM_RESOURCE_LIST ResourceList,
    IN     WCHAR const*      PnPId,
    IN OUT PULONG            Size
    )
{
    PCM_PARTIAL_RESOURCE_LIST       partialResourceList;
    PCM_PARTIAL_RESOURCE_DESCRIPTOR problemPartialDescriptors;
    PCM_PARTIAL_RESOURCE_DESCRIPTOR partialDescriptors;
    PCM_RESOURCE_LIST               newResourceList;
    ULONG                           i;

    newResourceList = ResourceList;

#if _X86_
    if (KeI386MachineType == MACHINE_TYPE_EISA) {

        PCM_FULL_RESOURCE_DESCRIPTOR    fullDescriptor;
        PCM_PARTIAL_RESOURCE_DESCRIPTOR partialDescriptor;
        PUCHAR                          nextDescriptor;
        ULONG                           j;
        ULONG                           lastResourceIndex;

        fullDescriptor = &ResourceList->List[0];

        for (i = 0; i < ResourceList->Count; i++) {

            partialResourceList = &fullDescriptor->PartialResourceList;

            for (j = 0; j < partialResourceList->Count; j++) {
                partialDescriptor = &partialResourceList->PartialDescriptors[j];

                if (partialDescriptor->Type == CmResourceTypePort) {
                    if (partialDescriptor->u.Port.Start.HighPart == 0 &&
                        (partialDescriptor->u.Port.Start.LowPart & 0x00000300) == 0) {
                        partialDescriptor->Flags |= CM_RESOURCE_PORT_16_BIT_DECODE;
                    }
                }
            }

            nextDescriptor = (PUCHAR)fullDescriptor + sizeof(CM_FULL_RESOURCE_DESCRIPTOR);

            //
            // account for any resource descriptors in addition to the single
            // imbedded one I've already accounted for (if there aren't any,
            // then I'll end up subtracting off the extra imbedded descriptor
            // from the previous step)
            //
            //
            // finally, account for any extra device specific data at the end of
            // the last partial resource descriptor (if any)
            //
            if (partialResourceList->Count > 0) {

                nextDescriptor += (partialResourceList->Count - 1) *
                     sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR);

                lastResourceIndex = partialResourceList->Count - 1;

                if (partialResourceList->PartialDescriptors[lastResourceIndex].Type ==
                          CmResourceTypeDeviceSpecific) {

                    nextDescriptor += partialResourceList->PartialDescriptors[lastResourceIndex].
                               u.DeviceSpecificData.DataSize;
                }
            }

            fullDescriptor = (PCM_FULL_RESOURCE_DESCRIPTOR)nextDescriptor;
        }
    }
#endif

    if (wcscmp(PnPId, FloppyId) == 0) {

        if (ResourceList->Count == 1) {

            partialResourceList = &ResourceList->List->PartialResourceList;

            partialDescriptors = partialResourceList->PartialDescriptors;

            //
            // Look for the one and only one 8 byte port resource
            //
            problemPartialDescriptors = NULL;
            for (i=0; i<partialResourceList->Count; i++) {

                if ((partialDescriptors[i].Type == CmResourceTypePort) &&
                    (partialDescriptors[i].u.Port.Length == 8)) {

                    if (problemPartialDescriptors == NULL) {

                        problemPartialDescriptors = partialDescriptors + i;
                    } else {

                        problemPartialDescriptors = NULL;
                        break;
                    }
                }
            }

            if (problemPartialDescriptors) {

                problemPartialDescriptors->u.Port.Length = 6;

                newResourceList = ExAllocatePool (
                                      NonPagedPool,
                                      *Size + sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR)
                                      );
                if (newResourceList) {

                    RtlCopyMemory (
                        newResourceList,
                        ResourceList,
                        *Size
                        );

                    //
                    // pick out the new partial resource descriptor
                    //
                    partialDescriptors = newResourceList->List->
                                             PartialResourceList.PartialDescriptors;
                    partialDescriptors += newResourceList->List->PartialResourceList.Count;

                    RtlCopyMemory (
                        partialDescriptors,
                        problemPartialDescriptors,
                        sizeof(*partialDescriptors)
                        );

                    partialDescriptors->u.Port.Start.QuadPart += 7;
                    partialDescriptors->u.Port.Length = 1;

                    //
                    // we got one more now
                    //
                    newResourceList->List->PartialResourceList.Count++;
                    *Size += sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR);

                    ExFreePool (ResourceList);

                } else {

                    newResourceList = ResourceList;
                }
            }
        }
    }

    return newResourceList;
}

NTSTATUS
ComPortDBAdd(
    IN  HANDLE  DeviceParamKey,
    IN  PWSTR   PortName
    )
{
    UNICODE_STRING                  portNameString;
    UNICODE_STRING                  portPrefixString;
    UNICODE_STRING                  comDBName;
    UNICODE_STRING                  valueName;
    PKEY_VALUE_PARTIAL_INFORMATION  valueInfo;
    ULONG                           valueInfoLength;
    ULONG                           returnedLength;
    HANDLE                          comDBKey;
    ULONG                           portNo;
    NTSTATUS                        status;

    RtlInitUnicodeString(&portNameString, PortName);

    if (portNameString.Length > 3 * sizeof(WCHAR)) {
        portNameString.Length = 3 * sizeof(WCHAR);
    }

    PiWstrToUnicodeString(&portPrefixString, L"COM");

    if (RtlCompareUnicodeString(&portNameString, &portPrefixString, TRUE) == 0) {
        portNo = _wtol(&PortName[3]);

        if (portNo > 0 && portNo <= 256) {

#if UMODETEST
            PiWstrToUnicodeString(&comDBName, L"\\Registry\\Machine\\System\\TestControlSet\\Control\\COM Name Arbiter");
#else
            PiWstrToUnicodeString(&comDBName, L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\COM Name Arbiter");
#endif

            status = IopCreateRegistryKeyEx( &comDBKey,
                                             NULL,
                                             &comDBName,
                                             KEY_ALL_ACCESS,
                                             REG_OPTION_NON_VOLATILE,
                                             NULL
                                             );

            if (NT_SUCCESS(status)) {

                PiWstrToUnicodeString(&valueName, L"ComDB Merge");

#define COMPORT_DB_MERGE_SIZE    32           //  256 / 8

                valueInfoLength = sizeof(KEY_VALUE_PARTIAL_INFORMATION) + COMPORT_DB_MERGE_SIZE;
                valueInfo = ExAllocatePool(PagedPool, valueInfoLength);

                if (valueInfo != NULL) {

                    status = ZwQueryValueKey( comDBKey,
                                              &valueName,
                                              KeyValuePartialInformation,
                                              valueInfo,
                                              valueInfoLength,
                                              &returnedLength);

                    if (status == STATUS_OBJECT_NAME_NOT_FOUND) {

                        valueInfo->Type = REG_BINARY;
                        valueInfo->DataLength = COMPORT_DB_MERGE_SIZE;
                        RtlZeroMemory(valueInfo->Data, valueInfo->DataLength);
                        status = STATUS_SUCCESS;
                    }

                    if (NT_SUCCESS(status)) {
                        portNo--;
                        valueInfo->Data[ portNo / 8 ] |= 1 << (portNo % 8);

                        status = ZwSetValueKey( comDBKey,
                                                &valueName,
                                                0,
                                                valueInfo->Type,
                                                valueInfo->Data,
                                                valueInfo->DataLength );

                        ASSERT(NT_SUCCESS(status));
                    }

                    ExFreePool(valueInfo);
                }

                ZwClose(comDBKey);
            }
        }
    }

    PiWstrToUnicodeString( &valueName, L"DosDeviceName" );

    status = ZwSetValueKey( DeviceParamKey,
                            &valueName,
                            0,
                            REG_SZ,
                            PortName,
                            (ULONG)((wcslen(PortName) + 1) * sizeof(WCHAR)) );

    return status;
}


VOID
MapperPhantomizeDetectedComPorts (
    VOID
    )
/*++

Routine Description:

    This routine turns all newly-created firmware/ntdetect COM ports into
    phantoms.

Arguments:

    None

Return Value:

    None

--*/
{
    PFIRMWARE_CONFIGURATION firmwareEntry;
    NTSTATUS                status;
    PWCHAR                  registryBase;
    PWCHAR                  instanceBuffer;
    HANDLE                  handle;
    PWCHAR                  buffer;
    PDEVICE_EXTENSION       DeviceExtension = &MapperDeviceExtension;
    UNICODE_STRING          enumKey;
    OBJECT_ATTRIBUTES       objectAttributes;
    UNICODE_STRING          unicodeName;
    ULONG                   regValue;

    //
    // allocate space needed for the registry path into the root
    // enumerator tree.  Note, limited size on path length.
    //

    buffer = ExAllocatePool(NonPagedPool, ENUM_KEY_BUFFER_SIZE);

    if (!buffer) {
        IopDbgPrint((IOP_MAPPER_ERROR_LEVEL,
                    "Mapper: could not allocate memory for registry update\n"));
        return;
    }

    instanceBuffer = ExAllocatePool(NonPagedPool, INSTANCE_BUFFER_SIZE);
    if (!instanceBuffer) {
        ExFreePool(buffer);
        IopDbgPrint((IOP_MAPPER_ERROR_LEVEL,
                    "Mapper: could not allocate memory for instance buffer\n"));
        return;
    }

    InitializeObjectAttributes(&objectAttributes,
                               &enumKey,
                               OBJ_CASE_INSENSITIVE,
                               NULL,
                               NULL);

#if UMODETEST
    registryBase = L"\\Registry\\Machine\\System\\TestControlSet\\Enum\\Root\\";
#else
    registryBase = L"\\Registry\\Machine\\System\\CurrentControlSet\\Enum\\Root\\";
#endif

    firmwareEntry = DeviceExtension->FirmwareList;
    while (firmwareEntry) {

        //
        // Construct the base for the path for this entry.
        //


        if ((firmwareEntry->ControllerType == SerialController) &&
            firmwareEntry->NewlyCreated) {

            PiWstrToUnicodeString(&enumKey, NULL);
            enumKey.MaximumLength = ENUM_KEY_BUFFER_SIZE;
            enumKey.Buffer = buffer;
            RtlZeroMemory(buffer, ENUM_KEY_BUFFER_SIZE);
            RtlAppendUnicodeToString(&enumKey, registryBase);
            RtlAppendUnicodeToString(&enumKey, firmwareEntry->PnPId);

            //
            // Construct the instance name.
            //

            RtlZeroMemory(instanceBuffer, INSTANCE_BUFFER_SIZE);
            swprintf(instanceBuffer,
                     L"\\%d_%d_%d_%d_%d_%d",
                     firmwareEntry->BusType,
                     firmwareEntry->BusNumber,
                     firmwareEntry->ControllerType,
                     firmwareEntry->ControllerNumber,
                     firmwareEntry->PeripheralType,
                     firmwareEntry->PeripheralNumber);
            RtlAppendUnicodeToString(&enumKey, instanceBuffer);

            status = ZwOpenKey(&handle,
                               KEY_READ | KEY_WRITE,
                               &objectAttributes
                              );

            if (NT_SUCCESS(status)) {

                PiWstrToUnicodeString(&unicodeName, REGSTR_VAL_PHANTOM);
                regValue = 1;
                ZwSetValueKey(handle,
                              &unicodeName,
                              0,
                              REG_DWORD,
                              &regValue,
                              sizeof(regValue)
                             );

                ZwClose(handle);
            }
        }

        firmwareEntry = firmwareEntry->Next;
    }

    ExFreePool (buffer);
    ExFreePool (instanceBuffer);
}
