//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this sample source code is subject to the terms of the Microsoft
// license agreement under which you licensed this sample source code. If
// you did not accept the terms of the license agreement, you are not
// authorized to use this sample source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the LICENSE.RTF on your install media or the root of your tools installation.
// THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES.
//

//
// Routines to Bus Support the PCIbus
//
#include <windows.h>
#include <types.h>
#include <pcienum.h>
#include <pcidbg.h>
#include <pcibus.h>
#include <CeBusCfg.h>

PciDeviceFolder::PciDeviceFolder(LPCTSTR lpBusName,LPCTSTR lpTemplateRegPath,DWORD dwBusType, DWORD BusNumber,PCI_SLOT_NUMBER SlotNumber,HANDLE hParent)
:   DeviceFolder(lpBusName,lpTemplateRegPath,dwBusType,BusNumber,SlotNumber.u.bits.DeviceNumber,SlotNumber.u.bits.FunctionNumber, hParent)
{
    m_CurPowerState=D0;
    m_CurSleepPowerState = D4;
    m_ConfigureIsBackuped = FALSE;;
    m_pCfg= new PCI_COMMON_CONFIG;
    m_fAlwaysBackup = FALSE;
    m_dwPowerMgrOffset = 0;
    FindPCIPowerMgrInterface();
}
PciDeviceFolder::~PciDeviceFolder()
{
    if (m_pCfg)
        delete m_pCfg;
}
// PCI Power Handling;
BOOL PciDeviceFolder::SetPowerState(CEDEVICE_POWER_STATE newPowerState)
{
    Lock();
    if (m_pCfg) {
        if (newPowerState==D4 && m_CurPowerState!=D4) { // Change from DX to D4. backup the configuration.
            m_ConfigureIsBackuped= BackupConfiguration();
        };
        if (newPowerState!=D4 && m_CurPowerState == D4) { // Out or D4, restore the configurate
            if (m_ConfigureIsBackuped) {
                RestoreConfiguration();
                m_ConfigureIsBackuped = FALSE;
            }
        }
    }
    if (newPowerState!=m_CurPowerState) {
        SetPciPowerState(newPowerState);
        m_CurPowerState=newPowerState;
    };
    // When SetPowerState is called. It means this driver is under Power Managment Control.
    // Sleep Power State is no longer D4. It is always sync with Currrent Power State.
    m_CurSleepPowerState = newPowerState;
    Unlock();
    return TRUE;
}
BOOL PciDeviceFolder::PowerUp()
{
    if (GetDeviceHandle()!=NULL &&  m_CurPowerState != m_CurSleepPowerState&& m_pCfg && m_ConfigureIsBackuped ) {
        SetPciPowerState(m_CurPowerState);
        if (m_CurSleepPowerState == D4 ||  m_fAlwaysBackup ) {
            RestoreConfiguration();
            m_ConfigureIsBackuped = FALSE;
        }
        SetPciPowerState(m_CurPowerState);
    }
    return TRUE;
}
BOOL PciDeviceFolder::PowerDown()
{
    if (GetDeviceHandle()!=NULL && m_CurPowerState != m_CurSleepPowerState && m_pCfg ) {
        if (m_CurSleepPowerState == D4 ||  m_fAlwaysBackup )
            m_ConfigureIsBackuped= BackupConfiguration();
        SetPciPowerState(m_CurSleepPowerState);                
    }
    return TRUE;
}
BOOL PciDeviceFolder::RestoreConfiguration()
{
    if (m_pCfg) {
        CE_BUS_DEVICE_CONFIGURATION_DATA deviceConfigData;
        deviceConfigData.lpDeviceBusName=NULL;
        deviceConfigData.dwSpace=PCI_WHICHSPACE_CONFIG;
        deviceConfigData.dwOffset=0;
        deviceConfigData.dwLength = sizeof(PCI_COMMON_CONFIG);
        deviceConfigData.pBuffer = m_pCfg;
        return SetConfigurationData(&deviceConfigData);
    }
    else
        return FALSE;
}
BOOL PciDeviceFolder::BackupConfiguration()
{
    if (m_pCfg) {
        CE_BUS_DEVICE_CONFIGURATION_DATA deviceConfigData;
        deviceConfigData.lpDeviceBusName=NULL;
        deviceConfigData.dwSpace=PCI_WHICHSPACE_CONFIG;
        deviceConfigData.dwOffset=0;
        deviceConfigData.dwLength = sizeof(PCI_COMMON_CONFIG);
        deviceConfigData.pBuffer = m_pCfg;
        m_ConfigureIsBackuped = GetConfigurationData(&deviceConfigData);
        return (m_ConfigureIsBackuped);
    }
    else
        return FALSE;
}
BOOL PciDeviceFolder::FindPCIPowerMgrInterface()
{
    PCI_COMMON_CONFIG pciCmnCfg;
    CE_BUS_DEVICE_CONFIGURATION_DATA deviceConfigData;
    deviceConfigData.lpDeviceBusName=NULL;
    deviceConfigData.dwSpace=PCI_WHICHSPACE_CONFIG;
    deviceConfigData.dwOffset=0;
    deviceConfigData.dwLength = sizeof(PCI_COMMON_CONFIG);
    deviceConfigData.pBuffer =  &pciCmnCfg;
    if (GetConfigurationData(&deviceConfigData)) { 
        if (pciCmnCfg.Status & 0x10) { // This one support Power Mgr.
            DWORD dwCapPtr = 0;
            // Search to find Power Mgr Register.
            switch (pciCmnCfg.HeaderType & 0x7f) {
            case PCI_DEVICE_TYPE: // PCI Bus Power Mgr Specification 3.1.1
                dwCapPtr = *(((PBYTE)&pciCmnCfg)+ 0x34);
                break;
            case PCI_BRIDGE_TYPE:
                dwCapPtr = *(((PBYTE)&pciCmnCfg)+ 0x34);
                break;
            case PCI_CARDBUS_TYPE:
                dwCapPtr = *(((PBYTE)&pciCmnCfg)+ 0x14);
                break;
            default:
                break;
            }
            if (dwCapPtr!=0 && dwCapPtr <= sizeof(pciCmnCfg) - sizeof(PCI_POWER_MANAGEMENT_BLOCK) ) {
                PPCI_POWER_MANAGEMENT_BLOCK pPmcBlock =(PPCI_POWER_MANAGEMENT_BLOCK)(((PBYTE)&pciCmnCfg) + dwCapPtr);
                // Search for PM Regs.
                while (pPmcBlock->CapabilityID!=0x1 && pPmcBlock->NextItemPtr != NULL
                        && pPmcBlock->NextItemPtr <= sizeof(pciCmnCfg) - sizeof(PCI_POWER_MANAGEMENT_BLOCK))  {
                    pPmcBlock = (PPCI_POWER_MANAGEMENT_BLOCK)(((PBYTE)&pciCmnCfg) + pPmcBlock->NextItemPtr);
                }
                if (pPmcBlock->CapabilityID==0x1 
                        && pPmcBlock->NextItemPtr <= sizeof(pciCmnCfg) - sizeof(PCI_POWER_MANAGEMENT_BLOCK))  {
                    // We found it.
                    m_dwPowerMgrOffset = (DWORD)((PBYTE)pPmcBlock - (PBYTE)&pciCmnCfg);
                    ASSERT( m_dwPowerMgrOffset < sizeof(pciCmnCfg)  - sizeof(PCI_POWER_MANAGEMENT_BLOCK));
                    CE_BUS_DEVICE_CONFIGURATION_DATA deviceConfigData1;
                    deviceConfigData1.lpDeviceBusName=NULL;
                    deviceConfigData1.dwSpace=PCI_WHICHSPACE_CONFIG;
                    deviceConfigData1.dwOffset= m_dwPowerMgrOffset ;
                    deviceConfigData1.dwLength = sizeof(m_PowerMgrBlock);
                    deviceConfigData1.pBuffer =  &m_PowerMgrBlock;
                    if (!GetConfigurationData(&deviceConfigData1)) { // If this fails. Make it un-spported.
                        m_dwPowerMgrOffset=0;
                    }
                    
                }
            }
        }
    }
    return  (m_dwPowerMgrOffset!=0);
}
BOOL PciDeviceFolder::SetPciPowerState(CEDEVICE_POWER_STATE curPowerState)
{
    BOOL bReturn = FALSE;
    if (m_dwPowerMgrOffset!=0) {
        CE_BUS_DEVICE_CONFIGURATION_DATA deviceConfigData;
        deviceConfigData.lpDeviceBusName=NULL;
        deviceConfigData.dwSpace=PCI_WHICHSPACE_CONFIG;
        deviceConfigData.dwOffset=0;
        deviceConfigData.dwLength = sizeof(m_PowerMgrBlock);
        deviceConfigData.pBuffer =  &m_PowerMgrBlock;
        if (!GetConfigurationData(&deviceConfigData)) { // If this fails. Make it un-spported.
            // PCI Power Mgr Spec 3.2.4
            DWORD dwPciState = 0;
            switch ( curPowerState) {
            case D0:
            default:
                dwPciState = 0;
                break;
            case D1:
                dwPciState = 1;
                break;
            case D2: 
                dwPciState = 2;
                break;
            case D3:  { // We only consern about wakeup source.
                // find next Higher state that can support wakeup source.
                DWORD dwBit = 0x4000; // Bit 14(D3) for D3 hot
                for (dwPciState = 3 ; dwPciState!=0; dwPciState -- ) {
                    if ((m_PowerMgrBlock.PMC & dwBit)!=0)
                        break;
                    dwBit >>=1;
                }
                break;
            }
            case D4:
                dwPciState = 3;
                break;
            }
            m_PowerMgrBlock.PMCSR = ((m_PowerMgrBlock.PMCSR) & (~0x3)) + (UINT16)(dwPciState & 0x3);
            deviceConfigData.lpDeviceBusName=NULL;
            deviceConfigData.dwSpace=PCI_WHICHSPACE_CONFIG;
            deviceConfigData.dwOffset=0;
            deviceConfigData.dwLength = sizeof(m_PowerMgrBlock);
            deviceConfigData.pBuffer =  &m_PowerMgrBlock;
            bReturn = SetConfigurationData(&deviceConfigData);
        }
    }
    return bReturn;
}
//-------------------------------------PCI Bus Driver -------------------------------------------------------
PciBusEnum::PciBusEnum(LPCTSTR lpActiveRegPath,LPCTSTR lpInstanceRegPath)
:   DefaultBusDriver(lpActiveRegPath)
,   m_ActiveKey(HKEY_LOCAL_MACHINE, lpActiveRegPath)
,   m_DeviceKey(HKEY_LOCAL_MACHINE,lpInstanceRegPath)
{
    DEBUGMSG(ZONE_ENUM| ZONE_INIT,(TEXT("BusEnum::BusEnum (ActivateRegPath=%s)\r\n"),lpActiveRegPath));
    // Get DEVLOAD_DEVKEY_VALNAME name
    m_lpActiveRegPath = NULL;
    DWORD dwKeyLen=0;
    if (lpInstanceRegPath!=NULL) {
        dwKeyLen = _tcslen(lpInstanceRegPath) + 1;
        m_lpActiveRegPath = new TCHAR [dwKeyLen];
        if ( m_lpActiveRegPath) {
            _tcscpy(m_lpActiveRegPath,lpInstanceRegPath);
            m_lpActiveRegPath [dwKeyLen-1]= 0;
        }
    }
        
    m_lpStrInitParam = NULL;
    m_dwNumOfInitParam =0 ;
    m_lpBusName = NULL;
    m_dwBusNumber = 0;
    m_dwDeviceIndex = 0;
    m_dwCfgAlwaysBackup = 0;

}
PciBusEnum::~PciBusEnum()
{
    if (m_lpStrInitParam )
        delete m_lpStrInitParam ;
    if ( m_lpBusName)
        delete  m_lpBusName;
    if (m_lpActiveRegPath) 
        delete m_lpActiveRegPath;
}
#define BUSNAMEUNKNOWN TEXT("UnknownBus")
BOOL PciBusEnum::Init()
{
    BOOL  bReturn = FALSE;
    // Initialize InitParam
    if ( m_lpActiveRegPath==NULL || !m_ActiveKey.IsKeyOpened()) {
        DEBUGMSG(ZONE_ENUM|ZONE_ERROR,(TEXT("PciBusEnum!Init RegOpenKeyEx() return FALSE!!!\r\n")));
        return FALSE;
    }
    if (m_lpStrInitParam == NULL ) {
        DWORD dwKeyLen=0;
        DWORD dwType;
        // BC. It is BC with RegEnum We may want remove this later
        // What this key contain multi-string that indicate the reigstry has such name will copy into active registry
        if (m_ActiveKey.RegQueryValueEx(DEVLOAD_REPARMS_VALNAME, &dwType,NULL,&dwKeyLen) && dwType== REG_MULTI_SZ && dwKeyLen!=0 ) {
            dwKeyLen = (dwKeyLen + sizeof(TCHAR) -1 ) / sizeof(TCHAR);
            m_lpStrInitParam = new TCHAR [ dwKeyLen ];
            if (m_lpStrInitParam) {
                if (m_ActiveKey.GetRegValue(DEVLOAD_REPARMS_VALNAME,(LPBYTE)m_lpStrInitParam,dwKeyLen * sizeof(TCHAR))) {
                    DWORD dwUnitCount= dwKeyLen;                        
                    for (LPTSTR lpCurPos = m_lpStrInitParam;
                            m_dwNumOfInitParam< MAX_INITPARAM && dwUnitCount!=0 && *lpCurPos!=0 ; 
                            m_dwNumOfInitParam++) {
                        m_lpInitParamArray[m_dwNumOfInitParam] = lpCurPos;
                        while (*lpCurPos!=0 && dwUnitCount!=0  ) {
                            lpCurPos++;
                            dwUnitCount --;
                        }
                        if (dwUnitCount) {
                            lpCurPos ++;
                            dwUnitCount --;
                        }
                        else 
                            break;
                    }
                            
                }
            }
        }
    }
    if (!m_ActiveKey.IsKeyOpened() || !m_ActiveKey.GetRegValue(PCI_ALWAYSBACKUP,(LPBYTE)m_dwCfgAlwaysBackup,sizeof(m_dwCfgAlwaysBackup))) {
        m_dwCfgAlwaysBackup = 0;
    }
    // Scan And Enum All the driver in registry.
    if ( GetDeviceHandle()!=NULL &&  m_DeviceKey.IsKeyOpened()) {
        DWORD dwKeyLen=0;
        DWORD dwType;
        if (!m_DeviceKey.GetRegValue(DEVLOAD_INTERFACETYPE_VALNAME,(PUCHAR) &m_dwBusType,sizeof(m_dwBusType))) {
            // No interface type defined
            m_dwBusType=InterfaceTypeUndefined;
        };
        dwKeyLen=0;
        if (m_lpBusName == NULL && m_DeviceKey.RegQueryValueEx(DEVLOAD_BUSNAME_VALNAME, &dwType,NULL,&dwKeyLen) && dwType== DEVLOAD_BUSNAME_VALTYPE && dwKeyLen!=0 ) {
            dwKeyLen = (dwKeyLen + sizeof(TCHAR) -1) / sizeof(TCHAR);
            m_lpBusName = new TCHAR[dwKeyLen];
            if (m_lpBusName ) {
                m_DeviceKey.GetRegValue(DEVLOAD_BUSNAME_VALNAME, (LPBYTE )m_lpBusName, dwKeyLen*sizeof(TCHAR) );
                m_lpBusName [dwKeyLen-1]=0;
            }
        }
        if (!m_DeviceKey.GetRegValue(DEVLOAD_BUSNUMBER_VALNAME , (LPBYTE)m_dwBusNumber,sizeof(m_dwBusNumber)))
            m_dwBusNumber=0;
        
        if ( AssignChildDriver())
            return TRUE;
    };
    DEBUGMSG(ZONE_ENUM|ZONE_ERROR,(TEXT("-BusENUM!Init eturn FALSE!!!\r\n")));
    return FALSE;
    
};
BOOL PciBusEnum::PostInit()
{
    BOOL bReturn = FALSE;
    if ( GetDeviceHandle()!=NULL &&  m_DeviceKey.IsKeyOpened()) {
        bReturn=ActiveAllChildDriver();
    };
    return bReturn;
}
DWORD PciBusEnum::GetBusNamePrefix(__out_ecount(dwSizeInUnit) LPTSTR lpReturnBusName,DWORD dwSizeInUnit)
{
    if (m_lpBusName && lpReturnBusName &&  dwSizeInUnit) {
        DWORD dwCopyUnit = min(_tcslen(m_lpBusName) +1 , dwSizeInUnit);
        _tcsncpy(lpReturnBusName,m_lpBusName,dwCopyUnit);
        lpReturnBusName[dwCopyUnit-1]=0;
        return dwCopyUnit;
    }
    else
        return DefaultBusDriver::GetBusNamePrefix(lpReturnBusName,dwSizeInUnit);
}

BOOL PciBusEnum::AssignChildDriver()
{
    DWORD NumSubKeys;
    DWORD MaxSubKeyLen;
    DWORD MaxClassLen;
    DWORD NumValues;
    DWORD MaxValueNameLen;
    DWORD MaxValueLen;
    // Get info on Template Key
    BOOL bSuccess = m_DeviceKey.RegQueryInfoKey(
                NULL,               // class name buffer (lpszClass)
                NULL,               // ptr to length of class name buffer (lpcchClass)
                NULL,               // reserved
                &NumSubKeys,        // ptr to number of sub-keys (lpcSubKeys)
                &MaxSubKeyLen,      // ptr to longest subkey name length (lpcchMaxSubKeyLen)
                &MaxClassLen,       // ptr to longest class string length (lpcchMaxClassLen)
                &NumValues,         // ptr to number of value entries (lpcValues)
                &MaxValueNameLen,  // ptr to longest value name length (lpcchMaxValueNameLen)
                &MaxValueLen,       // ptr to longest value data length (lpcbMaxValueData)
                NULL,               // ptr to security descriptor length
                NULL);              // ptr to last write time
                    
    if (!bSuccess) {
        DEBUGMSG(ZONE_ENUM|ZONE_ERROR,
            (TEXT("PCMCIA::RegCopyKey RegQueryInfoKey returned fails.\r\n")));
        return FALSE;
    }
    // Recurse for each sub-key
    
    for (DWORD Key = 0; Key < NumSubKeys; Key++) {
        // Get TKey sub-key according to Key
        WCHAR ValName[PCI_MAX_REG_NAME];
        DWORD ValLen = sizeof(ValName) / sizeof(WCHAR);
        if (! m_DeviceKey.RegEnumKeyEx(  Key, ValName, &ValLen, NULL,  NULL,  NULL, NULL)){
            DEBUGMSG(ZONE_ENUM,
                (TEXT("BusEnum::RegCopyKey RegEnumKeyEx(%d) returned Error\r\n"), ValName));
            break;
        }
        else {
            // Open sub-key under TKey
            CRegistryEdit TSubKey(m_DeviceKey.GetHKey(),ValName);
            if (!TSubKey.IsKeyOpened()) {
                DEBUGMSG(ZONE_ENUM|ZONE_ERROR,
                    (TEXT("PCIBUS::RegCopyKey RegOpenKeyEx(%s) returned Error\r\n"), ValName));
                
                continue;
            }
            else { // Get Bus Info.
                DDKPCIINFO dpi;
                dpi.cbSize = sizeof (DDKPCIINFO);                
                dpi.dwDeviceNumber = (DWORD)-1;
                dpi.dwFunctionNumber = (DWORD)-1;
                if (TSubKey.GetPciInfo(&dpi)!=ERROR_SUCCESS || dpi.dwDeviceNumber == (DWORD)-1 || dpi.dwFunctionNumber == (DWORD)-1) {
                    dpi.dwDeviceNumber = m_dwDeviceIndex++;
                    dpi.dwFunctionNumber = 0;
                }
                DWORD dwBusNumber = (DWORD)-1;
                if ( !TSubKey.GetRegValue(PCIBUS_BUSNUMBER_VALNAME, (LPBYTE)&dwBusNumber, sizeof(dwBusNumber)) || dwBusNumber == (DWORD)-1) {
                    dwBusNumber = m_dwBusNumber;
                }
                // We Create Foler for this driver.
                TCHAR lpChildPath[DEVKEY_LEN];
                _tcsncpy(lpChildPath,m_lpActiveRegPath,DEVKEY_LEN-1);
                lpChildPath[DEVKEY_LEN-2] = 0;
                DWORD dwLen=_tcslen(lpChildPath);
                lpChildPath[dwLen]=_T('\\');
                dwLen++;
                lpChildPath[dwLen]=0;
                _tcsncat(lpChildPath,ValName,DEVKEY_LEN-1-dwLen);
                lpChildPath[DEVKEY_LEN-1]=0;

                PCI_SLOT_NUMBER SlotNumber;
                SlotNumber.u.bits.DeviceNumber= dpi.dwDeviceNumber;
                SlotNumber.u.bits.FunctionNumber =  dpi.dwFunctionNumber;
                PciDeviceFolder * nDevice =
                    new PciDeviceFolder (m_lpBusName!=NULL?m_lpBusName:BUSNAMEUNKNOWN,
                            lpChildPath,m_dwBusType,dwBusNumber,SlotNumber,GetDeviceHandle());
                if (nDevice) {
                    nDevice->SetAlwaysBackup(m_dwCfgAlwaysBackup!=0);
                    InsertChild(nDevice);
                }
            }
            
        }
    }
    
    return TRUE;
}
#define MAX_TEMP_BUFFER_SIZE 0x200
BOOL PciBusEnum::ActiveAllChildDriver()
{
    TakeReadLock();
    PciDeviceFolder * pCurDevice = (PciDeviceFolder *)GetDeviceList ();
    while (pCurDevice) {
        DEBUGMSG(ZONE_ENUM,(TEXT("Activate Child: Template reg path is %s\r\n"),pCurDevice->GetRegPath()));
        // Create Initial Active Registry.
        for (DWORD dwIndex=0; dwIndex< m_dwNumOfInitParam; dwIndex++) {
            BYTE tempBuffer[ MAX_TEMP_BUFFER_SIZE] ;
            DWORD dwSize=MAX_TEMP_BUFFER_SIZE;
            DWORD dwType;
            if (m_DeviceKey.IsKeyOpened() && 
                    m_DeviceKey.RegQueryValueEx(m_lpInitParamArray[dwIndex],&dwType, tempBuffer,&dwSize) ) {
                REGINI Reg;
                Reg.lpszVal = m_lpInitParamArray[dwIndex];
                Reg.dwType = dwType;
                Reg.dwLen = dwSize;
                Reg.pData = tempBuffer;
                pCurDevice->AddInitReg(1, &Reg);
                DEBUGMSG(ZONE_ENUM,(TEXT("Activate Child add %s to %s ActivePath\r\n"),m_lpInitParamArray[dwIndex],pCurDevice->GetRegPath()));                
            }
        }
        pCurDevice =(PciDeviceFolder *) pCurDevice->GetNextDeviceFolder();
    }
    // Activate Device 
    DWORD dwCurOrder = 0;
    while (dwCurOrder != MAXDWORD) {
        DEBUGMSG(ZONE_ENUM,(TEXT("Activate Child LoaderDriver at order %d \r\n"),dwCurOrder));
        DWORD dwNextOrder = MAXDWORD;
        pCurDevice = (PciDeviceFolder *)GetDeviceList ();
        while (pCurDevice) {
            DWORD dwDeviceLoadOrder = pCurDevice->GetLoadOrder();
            if ( dwDeviceLoadOrder == dwCurOrder)
                pCurDevice->LoadDevice();
            else 
            if (dwDeviceLoadOrder> dwCurOrder  && dwDeviceLoadOrder < dwNextOrder)
                dwNextOrder = dwDeviceLoadOrder;
            pCurDevice = (PciDeviceFolder *)pCurDevice->GetNextDeviceFolder();
        }
        dwCurOrder = dwNextOrder;
    }
    LeaveReadLock();
    return TRUE;
}
BOOL PciBusEnum::PowerUp() 
{
    TakeReadLock();
    PciDeviceFolder * pCurDevice = (PciDeviceFolder *)GetDeviceList ();
    DWORD dwCurOrder = 0;
    while (dwCurOrder != MAXDWORD) {
        DEBUGMSG(ZONE_ENUM,(TEXT("PowerUp for order %d \r\n"),dwCurOrder));
        DWORD dwNextOrder = MAXDWORD;
        pCurDevice = (PciDeviceFolder *)GetDeviceList ();
        while (pCurDevice) {
            DWORD dwDeviceLoadOrder = pCurDevice->GetLoadOrder();
            if ( dwDeviceLoadOrder == dwCurOrder)
                pCurDevice->PowerUp();
            else 
            if (dwDeviceLoadOrder> dwCurOrder  && dwDeviceLoadOrder < dwNextOrder)
                dwNextOrder = dwDeviceLoadOrder;
            pCurDevice = (PciDeviceFolder *)pCurDevice->GetNextDeviceFolder();
        }
        dwCurOrder = dwNextOrder;
    }
    LeaveReadLock();    
    return TRUE;
}
BOOL PciBusEnum::PowerDown()
{
    TakeReadLock();
    PciDeviceFolder * pCurDevice =( PciDeviceFolder *) GetDeviceList ();
    DWORD dwCurOrder = MAXDWORD-1;
    while (dwCurOrder != MAXDWORD ) {
        DEBUGMSG(ZONE_ENUM,(TEXT("PowerDonw for order %d \r\n"),dwCurOrder));
        DWORD dwNextOrder = 0 ;
        pCurDevice = (PciDeviceFolder *)GetDeviceList ();
        while (pCurDevice) {
            DWORD dwDeviceLoadOrder = pCurDevice->GetLoadOrder();
            if ( dwDeviceLoadOrder == dwCurOrder)
                pCurDevice->PowerDown();
            else 
            if (dwDeviceLoadOrder< dwCurOrder  && dwDeviceLoadOrder > dwNextOrder)
                dwNextOrder = dwDeviceLoadOrder;
            pCurDevice =(PciDeviceFolder *) pCurDevice->GetNextDeviceFolder();
        }
        if (dwCurOrder==0 && dwNextOrder == 0 ) {
            dwCurOrder = MAXDWORD;
            break;
        }
        else
            dwCurOrder = dwNextOrder;
    }
    LeaveReadLock();
    return TRUE;
}
extern "C"
HANDLE CreatePciBus(LPCTSTR lpActiveRegPath,LPCTSTR lpInstanceRegPath)
{
    DefaultBusDriver * pBusDriver = new PciBusEnum(lpActiveRegPath,lpInstanceRegPath);
    if ( pBusDriver) {
        if ( pBusDriver->Init())
            return  (HANDLE)pBusDriver;
        else 
            delete  pBusDriver;
    }
    return NULL;
}


extern "C" void
DeletePciBus(HANDLE hInstance)
{
    DefaultBusDriver * pBusDriver = (DefaultBusDriver * )hInstance;
    if (pBusDriver )
        delete pBusDriver ;    
}
extern "C" BOOL 
PowerUp(DWORD dwData)
{
    DEBUGMSG(ZONE_INIT,(TEXT("Pcibus.DLL : +PowerUp dwData=%x \r\n"),dwData));
    DefaultBusDriver * pBusDriver = (DefaultBusDriver * )dwData;
    if (pBusDriver)
        pBusDriver->PowerUp();
    DEBUGMSG(ZONE_INIT,(TEXT("Pcibus.DLL : -PowerUp dwData=%x \r\n")));
    return TRUE;
}
extern "C" BOOL 
PowerDown(DWORD dwData)
{
    DEBUGMSG(ZONE_INIT,(TEXT("Pcibus.DLL : +PowerDown dwData=%x \r\n"),dwData));
    DefaultBusDriver * pBusDriver = (DefaultBusDriver * )dwData;
    if (pBusDriver)
        pBusDriver->PowerDown();
    DEBUGMSG(ZONE_INIT,(TEXT("Pcibus.DLL : -PowerDown dwData=%x \r\n")));
    return TRUE;
}
#define FASTBUSACCESS_TAG 0xDEFBa51e
typedef struct __FASTBUSACCESSHANDLE {
    DWORD dwHandleTag;
    DefaultBusDriver * pBusDriver;
    DeviceFolder     * pDeviceFolder;    
} FASTBUSACCESSHANDLE, *PFASTBUSACCESSHANDLE ;

extern "C" HANDLE
Open(
        HANDLE  pHead,          // @parm Handle returned by COM_Init.
        DWORD   AccessCode,     // @parm access code.
        DWORD   ShareMode       // @parm share mode - Not used in this driver.
        )
{
    DefaultBusDriver * pBusDriver = (DefaultBusDriver * )pHead;
    if (pBusDriver && pBusDriver->Open(AccessCode,ShareMode)) {
        PFASTBUSACCESSHANDLE pFastAccessHandle = new FASTBUSACCESSHANDLE;
        if (pFastAccessHandle) {
            pFastAccessHandle->dwHandleTag = FASTBUSACCESS_TAG;
            pFastAccessHandle->pBusDriver = pBusDriver;
            pFastAccessHandle->pDeviceFolder = NULL ; 
            return (HANDLE)pFastAccessHandle;
        }
        else 
            pBusDriver->Close();
    }
    return NULL;
}
extern "C" BOOL
Close(HANDLE pOpenHead)
{
    PFASTBUSACCESSHANDLE pFastAccessHandle = (PFASTBUSACCESSHANDLE) pOpenHead ;
    if (pFastAccessHandle && pFastAccessHandle->dwHandleTag ==FASTBUSACCESS_TAG ) {
        pFastAccessHandle->pBusDriver->Close();
        delete pFastAccessHandle ;
    }
    return FALSE;
}
extern "C" BOOL
IOControl(HANDLE pOpenHead,
              DWORD dwCode, PBYTE pBufIn,
              DWORD dwLenIn, PBYTE pBufOut, DWORD dwLenOut,
              PDWORD pdwActualOut)
{
    PFASTBUSACCESSHANDLE pFastAccessHandle = (PFASTBUSACCESSHANDLE) pOpenHead ;
    if (pFastAccessHandle && pFastAccessHandle->dwHandleTag ==FASTBUSACCESS_TAG )
        return pFastAccessHandle->pBusDriver->FastIOControl(dwCode,pBufIn,dwLenIn,pBufOut,dwLenOut,pdwActualOut,
            &(pFastAccessHandle->pDeviceFolder));
    return FALSE;
}


