/*++

Copyright (c) 1990  Microsoft Corporation

Module Name:

    PnpEisa.c

Abstract:

    This file implements Eisa related code.

Author:

    Shie-Lin Tzong (shielint)

Environment:

    Kernel Mode.

Notes:

Revision History:

--*/

#include "pnpmgrp.h"
#pragma hdrstop

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

#define EISA_DEVICE_NODE_NAME L"EisaResources"
#define BUFFER_LENGTH 50

NTSTATUS
EisaGetEisaDevicesResources (
    OUT PCM_RESOURCE_LIST *ResourceList,
    OUT PULONG ResourceLength
    );

NTSTATUS
EisaBuildSlotsResources (
    IN ULONG SlotMasks,
    IN ULONG NumberMasks,
    OUT PCM_RESOURCE_LIST *Resource,
    OUT ULONG *Length
    );

#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT, EisaBuildEisaDeviceNode)
#pragma alloc_text(INIT, EisaGetEisaDevicesResources)
#pragma alloc_text(INIT, EisaBuildSlotsResources)
#endif

NTSTATUS
EisaBuildEisaDeviceNode (
    VOID
    )

/*++

Routine Description:

    This routine build an registry key to report eisa resources to arbiters.

Arguments:

    None.

Return Value:

    NTSTATUS code.

--*/

{
    NTSTATUS            status;
    ULONG               disposition, tmpValue;
    WCHAR               buffer[BUFFER_LENGTH];

    UNICODE_STRING      unicodeString;
    HANDLE              rootHandle, deviceHandle, instanceHandle, logConfHandle;

    PCM_RESOURCE_LIST   resourceList;
    ULONG               resourceLength;

    status = EisaGetEisaDevicesResources(&resourceList, &resourceLength);
    if (!NT_SUCCESS(status) || resourceList == NULL) {
        return STATUS_UNSUCCESSFUL;
    }

    PiWstrToUnicodeString(&unicodeString, L"\\Registry\\Machine\\System\\CurrentControlSet\\Enum\\Root");
    status = IopOpenRegistryKeyEx( &rootHandle,
                                   NULL,
                                   &unicodeString,
                                   KEY_ALL_ACCESS
                                   );

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

    PiWstrToUnicodeString(&unicodeString, EISA_DEVICE_NODE_NAME);
    status = IopCreateRegistryKeyEx( &deviceHandle,
                                     rootHandle,
                                     &unicodeString,
                                     KEY_ALL_ACCESS,
                                     REG_OPTION_NON_VOLATILE,
                                     NULL
                                     );

    ZwClose(rootHandle);
    if (!NT_SUCCESS(status)) {
        if (resourceList) {
            ExFreePool (resourceList);
        }
        return status;
    }

    PiWstrToUnicodeString( &unicodeString, L"0000" );
    status = IopCreateRegistryKeyEx( &instanceHandle,
                                     deviceHandle,
                                     &unicodeString,
                                     KEY_ALL_ACCESS,
                                     REG_OPTION_NON_VOLATILE,
                                     &disposition );
    ZwClose(deviceHandle);
    if (NT_SUCCESS(status))  {

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

            status = IopGetRegistryValue(instanceHandle,
                                         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(instanceHandle, &unicodeString);
            }
        }

        if (disposition == REG_CREATED_NEW_KEY) {

            PiWstrToUnicodeString( &unicodeString, L"DeviceDesc" );
            wcsncpy(buffer, L"Device to report Eisa Slot Resources", sizeof(buffer) / sizeof(WCHAR));
            buffer[(sizeof(buffer) / sizeof(WCHAR)) - 1] = UNICODE_NULL;

            ZwSetValueKey(instanceHandle,
                          &unicodeString,
                          0,
                          REG_SZ,
                          buffer,
                          (ULONG)((wcslen(buffer) + 1) * sizeof(WCHAR))
                          );

            PiWstrToUnicodeString( &unicodeString, L"HardwareID" );
            RtlZeroMemory(buffer, BUFFER_LENGTH * sizeof(WCHAR));
            wcsncpy(buffer, L"*Eisa_Resource_Device", sizeof(buffer) / sizeof(WCHAR));
            buffer[(sizeof(buffer) / sizeof(WCHAR)) - 1] = UNICODE_NULL;

            ZwSetValueKey(instanceHandle,
                          &unicodeString,
                          0,
                          REG_MULTI_SZ,
                          buffer,
                          (ULONG)((wcslen(buffer) + 2) * sizeof(WCHAR))
                          );

            PiWstrToUnicodeString(&unicodeString, REGSTR_VALUE_CONFIG_FLAGS);
            tmpValue = 0;
            ZwSetValueKey(instanceHandle,
                         &unicodeString,
                         TITLE_INDEX_VALUE,
                         REG_DWORD,
                         &tmpValue,
                         sizeof(tmpValue)
                         );

        }

        PiWstrToUnicodeString( &unicodeString, REGSTR_KEY_LOGCONF );
        status = IopCreateRegistryKeyEx( &logConfHandle,
                                         instanceHandle,
                                         &unicodeString,
                                         KEY_ALL_ACCESS,
                                         REG_OPTION_NON_VOLATILE,
                                         NULL
                                         );
        ZwClose(instanceHandle);
        if (NT_SUCCESS(status))  {
            PiWstrToUnicodeString( &unicodeString, REGSTR_VAL_BOOTCONFIG );

            status = ZwSetValueKey(logConfHandle,
                                   &unicodeString,
                                   0,
                                   REG_RESOURCE_LIST,
                                   resourceList,
                                   resourceLength
                                   );
            ZwClose(logConfHandle);
        }
    }
    if (resourceList) {
        ExFreePool (resourceList);
    }
    return status;
}

NTSTATUS
EisaGetEisaDevicesResources (
    OUT PCM_RESOURCE_LIST *ResourceList,
    OUT PULONG ResourceLength
    )

/*++

Routine Description:

    This routine builds a cm resource list for all the eisa slots.

Arguments:

    None.

Return Value:

    A CmResourceList.

--*/

{
    NTSTATUS status = STATUS_SUCCESS;
    HANDLE handle;
    PKEY_VALUE_FULL_INFORMATION keyValueInformation;
    UNICODE_STRING unicodeString;
    ULONG slotMasks = 0, numberMasks = 0, i;

    *ResourceList = NULL;
    *ResourceLength = 0;

    //
    // Open LocalMachine\Hardware\Description
    //

    //PiWstrToUnicodeString(&unicodeString, L"\\REGISTRY\\MACHINE\\HARDWARE\\DESCRIPTION\\SYSTEM\\EisaAdapter\\0");
    PiWstrToUnicodeString(&unicodeString, L"\\REGISTRY\\MACHINE\\SYSTEM\\CurrentControlSet\\EisaAdapter");
    status = IopOpenRegistryKeyEx( &handle,
                                   NULL,
                                   &unicodeString,
                                   KEY_READ
                                   );
    if (NT_SUCCESS(status)) {
        status = IopGetRegistryValue(handle,
                                     L"Configuration Data",
                                     &keyValueInformation
                                     );
        if (NT_SUCCESS(status)) {
            PCM_FULL_RESOURCE_DESCRIPTOR resourceDescriptor;
            PCM_PARTIAL_RESOURCE_DESCRIPTOR partialResourceDescriptor;

            resourceDescriptor = (PCM_FULL_RESOURCE_DESCRIPTOR)
                ((PUCHAR) keyValueInformation + keyValueInformation->DataOffset);

            if ((keyValueInformation->DataLength >= sizeof(CM_FULL_RESOURCE_DESCRIPTOR)) &&
                (resourceDescriptor->PartialResourceList.Count > 0) ) {
                LONG eisaInfoLength;
                PCM_EISA_SLOT_INFORMATION eisaInfo;

                partialResourceDescriptor = resourceDescriptor->PartialResourceList.PartialDescriptors;
                if (partialResourceDescriptor->Type == CmResourceTypeDeviceSpecific) {
                    eisaInfo = (PCM_EISA_SLOT_INFORMATION)
                        ((PUCHAR)partialResourceDescriptor + sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR));
                    eisaInfoLength = (LONG)partialResourceDescriptor->u.DeviceSpecificData.DataSize;

                    //
                    // Parse the eisa slot info to find the eisa slots with device installed.
                    //

                    for (i = 0; i < 0x10 && eisaInfoLength > 0; i++) {
                        if (eisaInfo->ReturnCode == EISA_INVALID_SLOT) {
                            break;
                        }
                        if (eisaInfo->ReturnCode != EISA_EMPTY_SLOT && (i != 0)) {
                            slotMasks |= (1 << i);
                            numberMasks++;
                        }
                        if (eisaInfo->ReturnCode == EISA_EMPTY_SLOT) {
                            eisaInfoLength -= sizeof(CM_EISA_SLOT_INFORMATION);
                            eisaInfo++;
                        } else {
                            eisaInfoLength -= sizeof(CM_EISA_SLOT_INFORMATION) + eisaInfo->NumberFunctions * sizeof(CM_EISA_FUNCTION_INFORMATION);
                            eisaInfo = (PCM_EISA_SLOT_INFORMATION)
                                       ((PUCHAR)eisaInfo + eisaInfo->NumberFunctions * sizeof(CM_EISA_FUNCTION_INFORMATION) +
                                           sizeof(CM_EISA_SLOT_INFORMATION));
                        }
                    }

                    if (slotMasks) {
                        status = EisaBuildSlotsResources(slotMasks, numberMasks, ResourceList, ResourceLength);
                    }
                }

            }
            ExFreePool(keyValueInformation);
        }
        ZwClose(handle);
    }
    return status;
}

NTSTATUS
EisaBuildSlotsResources (
    IN ULONG SlotMasks,
    IN ULONG NumberMasks,
    OUT PCM_RESOURCE_LIST *Resources,
    OUT ULONG *Length
    )

/*++

Routine Description:

    This routine build a cm resource list for all the io resources used
    by the eisa devices.

Arguments:

    SlotMask - a mask to indicate the valid eisa slot.

Return Value:

    A pointer to a CM_RESOURCE_LIST.

--*/

{
    PCM_RESOURCE_LIST resources = NULL;
    PCM_PARTIAL_RESOURCE_DESCRIPTOR partialDesc;
    ULONG slot;

    *Length = sizeof(CM_RESOURCE_LIST) + sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR) * (NumberMasks - 1);
    resources = ExAllocatePool(PagedPool, *Length);
    if (resources) {
        resources->Count = 1;
        resources->List[0].InterfaceType = Eisa;
        resources->List[0].BusNumber = 0;
        resources->List[0].PartialResourceList.Version = 0;
        resources->List[0].PartialResourceList.Revision = 0;
        resources->List[0].PartialResourceList.Count = NumberMasks;
        partialDesc = resources->List[0].PartialResourceList.PartialDescriptors;
        slot = 0; // ignore slot 0
        while (SlotMasks) {
            SlotMasks >>= 1;
            slot++;
            if (SlotMasks & 1) {
                partialDesc->Type = CmResourceTypePort;
                partialDesc->ShareDisposition = CmResourceShareDeviceExclusive;
                partialDesc->Flags = CM_RESOURCE_PORT_16_BIT_DECODE + CM_RESOURCE_PORT_IO;
                partialDesc->u.Port.Start.LowPart = slot << 12;
                partialDesc->u.Port.Start.HighPart = 0;
                partialDesc->u.Port.Length = 0x1000;
                partialDesc++;
            }
        }
        *Resources = resources;
        return STATUS_SUCCESS;
    } else {
        return STATUS_NO_MEMORY;
    }
}