/*++

Copyright (c) 1998 Microsoft Corporation

Module Name:

    pcihal.c

Abstract:

    Routines for the Pci Hal property page.

Author:

    Santosh Jodh 10-July-1998

--*/

#include "setupp.h"
#pragma hdrstop
#include <windowsx.h>

#define MSG_SIZE    2048

#define Allocate(n) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, n)
#define Release(p)  HeapFree(GetProcessHeap(), 0, (LPVOID)p)

typedef struct _PciHalPropData PCIHALPROPDATA, *PPCIHALPROPDATA;

struct _PciHalPropData {
    HKEY                LocalMachine;
    BOOLEAN             CloseKey;
    DWORD               Options;
    HDEVINFO            DeviceInfoSet;
    PSP_DEVINFO_DATA    DeviceInfoData;    
};  

const DWORD gPciPropHelpIds[] = 
{
    IDC_PCIHAL_ICON,        (DWORD)-1,              // Icon
    IDC_PCIHAL_DEVDESC,     (DWORD)-1,              // Name of PC
    IDC_PCIHAL_ENABLE,      IDH_IRQ_ENABLE,         // Enable IRQ Routing
    IDC_PCIHAL_MSSPEC,      IDH_IRQ_MSSPEC,         // Use $PIR table
    IDC_PCIHAL_REALMODE,    IDH_IRQ_REALMODE,       // Use table from Real-mode BIOS call
    IDC_PCIHAL_SETDEFAULTS, IDH_IRQ_SETDEFAULTS,    // Set defaults for options
    IDC_PCIHAL_RESULTS,     IDH_IRQ_RESULTS,        // Status information    
    0,0
};

//
// Table used to translate status codes into string ids.
//
UINT gStatus[PIR_STATUS_MAX + 1] =              {   IDS_PCIHAL_ERROR, 
                                                    IDS_PCIHAL_ENABLED, 
                                                    IDS_PCIHAL_DISABLED,
                                                    IDS_PCIHAL_NOSTATUS
                                                };
UINT gTableStatus[PIR_STATUS_TABLE_MAX] =       {   IDS_PCIHAL_TABLE_REGISTRY, 
                                                    IDS_PCIHAL_TABLE_MSSPEC,
                                                    IDS_PCIHAL_TABLE_REALMODE,
                                                    IDS_PCIHAL_TABLE_NONE,
                                                    IDS_PCIHAL_TABLE_ERROR,                                                    
                                                    IDS_PCIHAL_TABLE_BAD,
                                                    IDS_PCIHAL_TABLE_SUCCESS
                                                };
UINT gMiniportStatus[PIR_STATUS_MINIPORT_MAX] = {   IDS_PCIHAL_MINIPORT_NORMAL, 
                                                    IDS_PCIHAL_MINIPORT_COMPATIBLE, 
                                                    IDS_PCIHAL_MINIPORT_OVERRIDE, 
                                                    IDS_PCIHAL_MINIPORT_NONE,
                                                    IDS_PCIHAL_MINIPORT_ERROR,                                                    
                                                    IDS_PCIHAL_MINIPORT_NOKEY,
                                                    IDS_PCIHAL_MINIPORT_SUCCESS,
                                                    IDS_PCIHAL_MINIPORT_INVALID
                                                };

PCIHALPROPDATA  gPciHalPropData = {0};

VOID
PciHalSetControls (
    IN HWND Dialog,
    IN DWORD Options,
    IN DWORD Attributes
    )

/*++

    Routine Description:

        This routine sets the controls on the Irq Routing page to the
        specified options.

    Input Parameters:

        Dialog - Window handle for the property sheet page.

        Options -  Pci Irq Routing options to be displayed.
        
    Return Value:

        None.
        
--*/

{
    BOOL enabled = FALSE;

    //
    // Enable the buttons depending on the options.
    //
    if (Options & PIR_OPTION_ENABLED)
    {
        enabled = TRUE;
        CheckDlgButton(Dialog, IDC_PCIHAL_ENABLE, 1);
    }

    CheckDlgButton(Dialog, IDC_PCIHAL_MSSPEC, Options & PIR_OPTION_MSSPEC);
    CheckDlgButton(Dialog, IDC_PCIHAL_REALMODE, Options & PIR_OPTION_REALMODE);
    

    //
    // Gray the windows not meaningful.
    //    
    EnableWindow(GetDlgItem(Dialog, IDC_PCIHAL_ENABLE), !(Attributes & PIR_OPTION_ENABLED));
    EnableWindow(GetDlgItem(Dialog, IDC_PCIHAL_SETDEFAULTS), !(Attributes & PIR_OPTION_ENABLED));
    EnableWindow(GetDlgItem(Dialog, IDC_PCIHAL_MSSPEC), enabled && !(Attributes & PIR_OPTION_MSSPEC));
    EnableWindow(GetDlgItem(Dialog, IDC_PCIHAL_REALMODE), enabled && !(Attributes & PIR_OPTION_REALMODE));

}

LPTSTR
PciHalGetDescription (
    IN  HDEVINFO         DeviceInfoSet,
    IN  PSP_DEVINFO_DATA DeviceInfoData
    )

/*++

    Routine Description:

        This routine allocates memory and returns the device description
        for the specified device.

    Input Parameters:

        DeviceInfoSet - For the device.

        DeviceInfoData - For the device.

    Return Value:

        Pointer to the description iff successful. Else NULL.
        
--*/

{
    LPTSTR desc;
    DWORD   size;
    
    desc = NULL;
    
    //
    // Get the size of the description for this device.
    //
    size = 0;
    SetupDiGetDeviceRegistryProperty(   DeviceInfoSet,
                                        DeviceInfoData,
                                        SPDRP_DEVICEDESC,
                                        NULL,
                                        NULL,
                                        0,
                                        &size);

    if (size != 0)
    {
        //
        // Account for the terminating NULL character.
        //
        size++;
        
        //
        // Allocate memory for the device description.
        //
        desc = Allocate(size * sizeof(TCHAR));

        if (desc != NULL)
        {

            //
            // Get the device description.
            //
            if (SetupDiGetDeviceRegistryProperty(   DeviceInfoSet,
                                                    DeviceInfoData,
                                                    SPDRP_DEVICEDESC,
                                                    NULL,
                                                    (PBYTE)desc,
                                                    size * sizeof(TCHAR),
                                                    &size) == FALSE)
            {
                Release(desc);
                desc = NULL;
            }
        }
    }

    return desc;
}

LPTSTR
PciHalGetStatus (
    IN DWORD Status,
    IN DWORD TableStatus,
    IN DWORD MiniportStatus
    )

/*++

    Routine Description:

        This routine converts the different status codes into
        a status string and returns the pointer to the string.
        The caller should free the memory when done using this
        string.

    Input Parameters:

        Status - Pci Irq Routing status.

        TableStatus - Pci Irq Routing Table status. Lower WORD
        indicates the source of the table. The upper WORD indicates
        the table processing status.

        MiniportStatus - Pci Irq Routing Miniport status. Lower
        WORD indicates the source of the miniport. The upper WORD
        indicates the miniport processing status.

    Return Value:

        Pointer to the status string iff successful. Else NULL.
        
--*/

{
    LPTSTR   status;
    TCHAR   temp[128];

    status = Allocate(MSG_SIZE * sizeof(TCHAR));
    if (status)
    {
        //
        // Get the status about Pci Irq Routing.
        //
        LoadString(MyModuleHandle, gStatus[Status], status, MSG_SIZE);        

        //
        // Get the status about the source of Pci Irq Routing Table.
        //
        if ((TableStatus & 0xFFFF) < PIR_STATUS_TABLE_MAX)
        {
            lstrcat(status, L"\r\n\r\n");
            LoadString(MyModuleHandle, gTableStatus[TableStatus & 0xFFFF], temp, sizeof(temp)/sizeof(temp[0]));
            lstrcat(status, temp);
        }

        //
        // Get the status about the Pci Irq Routing table.
        //
        TableStatus >>= 16;
        if (TableStatus < PIR_STATUS_TABLE_MAX)
        {
            lstrcat(status, L"\r\n\r\n");
            LoadString(MyModuleHandle, gTableStatus[TableStatus], temp, sizeof(temp) / sizeof(TCHAR));
            lstrcat(status, temp);
        }

        //
        // Get the status about the source of the miniport.
        //
        if ((MiniportStatus & 0xFFFF) < PIR_STATUS_MINIPORT_MAX)
        {
            lstrcat(status, L"\r\n\r\n");
            LoadString(MyModuleHandle, gMiniportStatus[MiniportStatus & 0xFFFF], temp, sizeof(temp) / sizeof(TCHAR));
            lstrcat(status, temp);
        }

        //
        // Get the status about the miniport status.
        //
        MiniportStatus >>= 16;
        if (MiniportStatus < PIR_STATUS_MINIPORT_MAX)
        {
            lstrcat(status, L"\r\n\r\n");
            LoadString(MyModuleHandle, gMiniportStatus[MiniportStatus], temp, sizeof(temp) / sizeof(TCHAR));
            lstrcat(status, temp);
        }
    }

    return status;
}

BOOL
PciHalOnInitDialog (
    IN HWND Dialog,
    IN WPARAM wParam,
    IN LPARAM lParam
    )

/*++

    Routine Description:

        This routine initializes the property sheet page on creation.
        
    Input Paramters:

        Dialog - Window handle for the property sheet page.

        wParam - wParam of the WM_INITDIALOG message.

        lParam - Pointer to the property sheet page.
        
    Return Value:

        TRUE.
        
--*/

{   
    PPCIHALPROPDATA             pciHalPropData;
    HKEY                        hKey;
    DWORD                       size;    
    DWORD                       status;
    DWORD                       tableStatus;
    DWORD                       miniportStatus;
    DWORD                       attributes;
    HICON                       hIconOld;
    HICON                       hIconNew;
    INT                         iconIndex;
    LPTSTR                      desc;
    SP_DEVINFO_LIST_DETAIL_DATA details;
    
    pciHalPropData = (PPCIHALPROPDATA)((LPPROPSHEETPAGE)lParam)->lParam;
    //
    // Read the Pci Irq Routing options and status from the registry.
    //
    pciHalPropData->Options = 0;
    status = PIR_STATUS_MAX;
    tableStatus = PIR_STATUS_TABLE_MAX | (PIR_STATUS_TABLE_MAX << 16);
    miniportStatus = PIR_STATUS_MINIPORT_MAX | (PIR_STATUS_MINIPORT_MAX << 16);
    details.cbSize = sizeof(SP_DEVINFO_LIST_DETAIL_DATA);
    attributes = PIR_OPTION_ENABLED | PIR_OPTION_MSSPEC | PIR_OPTION_REALMODE;
    if (SetupDiGetDeviceInfoListDetail(pciHalPropData->DeviceInfoSet, &details)) {

        if (RegConnectRegistry((details.RemoteMachineName[0] == TEXT('\0'))? NULL : details.RemoteMachineName, HKEY_LOCAL_MACHINE, &pciHalPropData->LocalMachine) == ERROR_SUCCESS) {

            pciHalPropData->CloseKey = TRUE;
            if (RegOpenKeyEx(pciHalPropData->LocalMachine, REGSTR_PATH_PCIIR, 0, KEY_READ, &hKey) == ERROR_SUCCESS) 
            { 
                size = sizeof(pciHalPropData->Options);
                RegQueryValueEx(hKey, REGSTR_VAL_OPTIONS, NULL, NULL, (LPBYTE)&pciHalPropData->Options, &size);

                size = sizeof(status);
                RegQueryValueEx(hKey, REGSTR_VAL_STAT, NULL, NULL, (LPBYTE)&status, &size);

                size = sizeof(tableStatus);
                RegQueryValueEx(hKey, REGSTR_VAL_TABLE_STAT, NULL, NULL, (LPBYTE)&tableStatus, &size);

                size = sizeof(miniportStatus);
                RegQueryValueEx(hKey, REGSTR_VAL_MINIPORT_STAT, NULL, NULL, (LPBYTE)&miniportStatus, &size);

                RegCloseKey(hKey);
            }

            //
            // Gray out the controls if the user does not have READ+WRITE access to the REGSTR_PATH_PCIIR. 
            //

            if (RegOpenKeyEx(pciHalPropData->LocalMachine, REGSTR_PATH_PCIIR, 0, KEY_READ | KEY_WRITE, &hKey) == ERROR_SUCCESS) {

                RegCloseKey(hKey);
                attributes = 0;
                if (RegOpenKeyEx(pciHalPropData->LocalMachine, REGSTR_PATH_BIOSINFO L"\\PciIrqRouting", 0, KEY_READ, &hKey) == ERROR_SUCCESS) { 

                    size = sizeof(attributes);
                    RegQueryValueEx(hKey, L"Attributes", NULL, NULL, (LPBYTE)&attributes, &size);
                    RegCloseKey(hKey);
                }
            }
        }
    }

    //
    // Set the class icon.
    //
    if (SetupDiLoadClassIcon(   &pciHalPropData->DeviceInfoData->ClassGuid, 
                                &hIconNew, 
                                &iconIndex) == TRUE)
    {
        hIconOld = (HICON)SendDlgItemMessage(   Dialog, 
                                                IDC_PCIHAL_ICON, 
                                                STM_SETICON,
                                                (WPARAM)hIconNew,
                                                0);
        if (hIconOld)                                                
        {
            DestroyIcon(hIconOld);
        }
    }

    //
    // Set the device description.
    //
    desc = PciHalGetDescription(pciHalPropData->DeviceInfoSet, pciHalPropData->DeviceInfoData);
    if (desc)
    {
        SetDlgItemText(Dialog, IDC_PCIHAL_DEVDESC, desc);
        Release(desc);
    }

    //
    // Set the initial state of the controls.
    //
    PciHalSetControls(Dialog, pciHalPropData->Options, attributes);

    //
    // Display status.
    //
    desc = PciHalGetStatus(status, tableStatus, miniportStatus);
    if (desc)
    {
        SetDlgItemText(Dialog, IDC_PCIHAL_RESULTS, desc);
        Release(desc);
    }

    //
    // Let the system set the focus.
    //
    
    return TRUE;
    }

BOOL
PciHalOnCommand (
    IN HWND Dialog,
    IN WPARAM wParam,
    IN LPARAM lParam
    )

/*++

    Routine Description:

        This routine handles the message when the user modifies something
        on the property sheet page.

    Input Parameters:

        Dialog - Window handle for the property sheet page.

        wParam - wParam of the WM_COMMAND message.

        lParam - lParam of the WM_COMMAND message.
        
    Return Value:

        TRUE if this function handles the message. Else FALSE.
    
--*/

{
    BOOL status;
    BOOL enabled;

    status = FALSE;
    
    switch (GET_WM_COMMAND_ID(wParam, lParam))
    {
        case IDC_PCIHAL_SETDEFAULTS:

            //
            // Set the controls to the default value.
            //
            status = TRUE;
            PciHalSetControls(Dialog, PIR_OPTION_DEFAULT, 0);
            break;

        case IDC_PCIHAL_ENABLE:

            //
            // Gray out the sub-options if Irq Routing is being disabled.
            //            
            status = TRUE;
            enabled = IsDlgButtonChecked(Dialog, IDC_PCIHAL_ENABLE);
            EnableWindow(GetDlgItem(Dialog, IDC_PCIHAL_MSSPEC), enabled);
            EnableWindow(GetDlgItem(Dialog, IDC_PCIHAL_REALMODE), enabled);            
            break;

        default:
        
            break;
    }

    return status;
}

BOOL
PciHalOnNotify(
    IN HWND Dialog,
    IN WPARAM wParam,
    IN LPARAM lParam
    )

/*++

    Routine Description:

        This routine handles the WM_NOTIFY message for the Pci Irq Routing
        property sheet page.

    Input Parameters:

        Dialog - Window handle for the property sheet page.

        wParam - wParam of the WM_NOTIFY message.

        lParam - lParam of the WM_NOTIFY message.
    
    Return Value:

        TRUE if this function handles the message. Else FALSE.
        
--*/

{
    BOOL                    status = FALSE;
    HKEY                    hKey;
    DWORD                   options;
    
    switch (((LPNMHDR)lParam)->code)
    {
        case PSN_RESET:        

            //
            // User hit cancel.
            //
            status = TRUE;

            if (RegOpenKey(gPciHalPropData.LocalMachine, REGSTR_PATH_PCIIR, &hKey) == ERROR_SUCCESS)
            {
                RegSetValueEx(  hKey, 
                                REGSTR_VAL_OPTIONS, 
                                0, 
                                REG_DWORD, 
                                (CONST BYTE *)&gPciHalPropData.Options, 
                                sizeof(gPciHalPropData.Options));
                RegCloseKey(hKey);
            }
            
            break;
            
        case PSN_APPLY:

            //
            // User hit Apply or Ok.
            //
            status = TRUE;
            
            //
            // Read the different control status and write it to the registry.
            //
            options = gPciHalPropData.Options;
            if (IsDlgButtonChecked(Dialog, IDC_PCIHAL_ENABLE) == BST_CHECKED)
            {
                options |= PIR_OPTION_ENABLED;
            }
            else
            {
                options &= ~PIR_OPTION_ENABLED;
            }

            if (IsDlgButtonChecked(Dialog, IDC_PCIHAL_MSSPEC))
            {
                options |= PIR_OPTION_MSSPEC;
            }
            else
            {
                options &= ~PIR_OPTION_MSSPEC;
            }

            if (IsDlgButtonChecked(Dialog, IDC_PCIHAL_REALMODE))
            {
                options |= PIR_OPTION_REALMODE;
            }
            else
            {
                options &= ~PIR_OPTION_REALMODE;
            }

            if (RegOpenKey(gPciHalPropData.LocalMachine, REGSTR_PATH_PCIIR, &hKey) == ERROR_SUCCESS)
            {
                RegSetValueEx(  hKey, 
                                REGSTR_VAL_OPTIONS, 
                                0, 
                                REG_DWORD, 
                                (CONST BYTE *)&options, 
                                sizeof(options));
                RegCloseKey(hKey);
            }

            //
            // Reboot if any of the options changed.
            //
            if (options != gPciHalPropData.Options)
            {
                SP_DEVINSTALL_PARAMS    deviceInstallParams;

                memset(&deviceInstallParams, 0, sizeof(deviceInstallParams));
                deviceInstallParams.cbSize = sizeof(deviceInstallParams);
                if (SetupDiGetDeviceInstallParams(  gPciHalPropData.DeviceInfoSet, 
                                                    gPciHalPropData.DeviceInfoData, 
                                                    &deviceInstallParams))
                {
                    deviceInstallParams.Flags |= DI_NEEDREBOOT;
                    SetupDiSetDeviceInstallParams(  gPciHalPropData.DeviceInfoSet, 
                                                    gPciHalPropData.DeviceInfoData, 
                                                    &deviceInstallParams);
                    
                }
            }
            
            break;

        default:

            break;
    }

    return status;
}

INT_PTR
PciHalDialogProc(
    IN HWND Dialog,
    IN UINT Message,
    IN WPARAM wParam,
    IN LPARAM lParam
    )

/*++

    Routine Description:

        This is the DlgProc for the Pci Irq Routing property sheet page.
        
    Input Parameters:

        Standard DlgProc parameters.
        
    Return Value:

        TRUE if it handles the message. Else FALSE.
        
--*/

{
    BOOL    status = FALSE;
    PCWSTR  szHelpFile = L"devmgr.hlp";
    
    switch (Message)
    {
        case WM_INITDIALOG:

            status = PciHalOnInitDialog(Dialog, wParam, lParam);
            break;

        case WM_COMMAND:

            status = PciHalOnCommand(Dialog, wParam, lParam);
            break;

        case WM_NOTIFY:

            status = PciHalOnNotify(Dialog, wParam, lParam);
            break;

        case WM_HELP:
            
            WinHelp((HWND)((LPHELPINFO)lParam)->hItemHandle, szHelpFile, HELP_WM_HELP, (ULONG_PTR)gPciPropHelpIds);
            status = TRUE;
            break;
            
        case WM_CONTEXTMENU:

            WinHelp((HWND)wParam, szHelpFile, HELP_CONTEXTMENU, (ULONG_PTR)gPciPropHelpIds);
            status = TRUE;
            break;

        case WM_DESTROY:

            if (gPciHalPropData.CloseKey) {
                RegCloseKey(gPciHalPropData.LocalMachine);
                gPciHalPropData.CloseKey = FALSE;
            }
        
        default:

            break;
    }

    return status;
}

DWORD
PciHalCoInstaller(
    IN DI_FUNCTION                      InstallFunction,
    IN HDEVINFO                         DeviceInfoSet,
    IN PSP_DEVINFO_DATA                 DeviceInfoData  OPTIONAL,
    IN OUT PCOINSTALLER_CONTEXT_DATA    Context
    )
{
    BOOL                        status;
    HPROPSHEETPAGE              pageHandle;
    PROPSHEETPAGE               page;
    SP_DEVINFO_LIST_DETAIL_DATA details;
    SP_ADDPROPERTYPAGE_DATA     addPropertyPageData;

    switch (InstallFunction) {
    case DIF_ADDPROPERTYPAGE_ADVANCED:        
    case DIF_ADDREMOTEPROPERTYPAGE_ADVANCED:

        details.cbSize = sizeof(SP_DEVINFO_LIST_DETAIL_DATA);
        if (SetupDiGetDeviceInfoListDetail(DeviceInfoSet, &details)) {

            if (RegConnectRegistry((details.RemoteMachineName[0] == TEXT('\0'))? NULL : details.RemoteMachineName, HKEY_LOCAL_MACHINE, &gPciHalPropData.LocalMachine) == ERROR_SUCCESS) {

                RegCloseKey(gPciHalPropData.LocalMachine);
                status = TRUE;
                break;
            }
        }

    default:

        status = FALSE;
        break;
    }
    if (status) {

        ZeroMemory(&addPropertyPageData, sizeof(SP_ADDPROPERTYPAGE_DATA));
        addPropertyPageData.ClassInstallHeader.cbSize = 
             sizeof(SP_CLASSINSTALL_HEADER);

        if (SetupDiGetClassInstallParams(DeviceInfoSet, DeviceInfoData,
             (PSP_CLASSINSTALL_HEADER)&addPropertyPageData,
             sizeof(SP_ADDPROPERTYPAGE_DATA), NULL )) {

           if (addPropertyPageData.NumDynamicPages < MAX_INSTALLWIZARD_DYNAPAGES) {

               //
               // Initialize our globals here.
               //
               gPciHalPropData.DeviceInfoSet    = DeviceInfoSet;
               gPciHalPropData.DeviceInfoData   = DeviceInfoData;

               //
               // Initialize our property page here.
               //
               memset(&page, 0, sizeof(PROPSHEETPAGE));
               page.dwSize      = sizeof(PROPSHEETPAGE);
               page.hInstance   = MyModuleHandle;
               page.pszTemplate = MAKEINTRESOURCE(IDD_PCIHAL_PROPPAGE);
               page.pfnDlgProc  = PciHalDialogProc;
               page.lParam      = (LPARAM)&gPciHalPropData;

               pageHandle = CreatePropertySheetPage(&page);
               if (pageHandle != NULL)
               {

                   addPropertyPageData.DynamicPages[addPropertyPageData.NumDynamicPages++] = pageHandle;
                    SetupDiSetClassInstallParams(DeviceInfoSet, DeviceInfoData,
                        (PSP_CLASSINSTALL_HEADER)&addPropertyPageData,
                        sizeof(SP_ADDPROPERTYPAGE_DATA));

                    return NO_ERROR;
               }
           }
        }
    }

    return NO_ERROR;
}