www.pudn.com > 9054Src.rar > SupportFunc.c


/******************************************************************************* 
 * Copyright (c) 2006 PLX Technology, Inc. 
 * 
 * PLX Technology Inc. licenses this software under specific terms and 
 * conditions.  Use of any of the software or derviatives thereof in any 
 * product without a PLX Technology chip is strictly prohibited. 
 * 
 * PLX Technology, Inc. provides this software AS IS, WITHOUT ANY WARRANTY, 
 * EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, ANY WARRANTY OF 
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.  PLX makes no guarantee 
 * or representations regarding the use of, or the results of the use of, 
 * the software and documentation in terms of correctness, accuracy, 
 * reliability, currentness, or otherwise; and you rely on the software, 
 * documentation and results solely at your own risk. 
 * 
 * IN NO EVENT SHALL PLX BE LIABLE FOR ANY LOSS OF USE, LOSS OF BUSINESS, 
 * LOSS OF PROFITS, INDIRECT, INCIDENTAL, SPECIAL OR CONSEQUENTIAL DAMAGES 
 * OF ANY KIND.  IN NO EVENT SHALL PLX'S TOTAL LIABILITY EXCEED THE SUM 
 * PAID TO PLX FOR THE PRODUCT LICENSED HEREUNDER. 
 * 
 ******************************************************************************/ 
 
/****************************************************************************** 
 * 
 * File Name: 
 * 
 *      SupportFunc.c 
 * 
 * Description: 
 * 
 *      Additional support functions 
 * 
 * Revision History: 
 * 
 *      03-01-06 : PCI SDK v4.40 
 * 
 ******************************************************************************/ 
 
 
#include "ApiFunctions.h" 
#include "CommonApi.h" 
#include "GlobalVars.h" 
#include "PciSupport.h" 
#include "SupportFunc.h" 
 
 
 
 
/********************************************************************* 
 * 
 * Function   :  PlxSynchronizedRegisterModify 
 * 
 * Description:  Register modify to be called with KeSynchronizeExecution() 
 * 
 ********************************************************************/ 
BOOLEAN 
PlxSynchronizedRegisterModify( 
    PLX_REG_DATA *pRegData 
    ) 
{ 
    U32 RegValue; 
 
 
    RegValue = 
        PLX_REG_READ( 
            pRegData->pdx, 
            pRegData->offset 
            ); 
 
    RegValue |= pRegData->BitsToSet; 
    RegValue &= ~(pRegData->BitsToClear); 
 
    PLX_REG_WRITE( 
        pRegData->pdx, 
        pRegData->offset, 
        RegValue 
        ); 
 
    return TRUE; 
} 
 
 
 
 
/****************************************************************************** 
 * 
 * Function   :  PlxSignalNotifications 
 * 
 * Description:  Called by the ISR to signal any notification events 
 * 
 * Note       :  This is expected to be called at DPC level 
 * 
 ******************************************************************************/ 
VOID 
PlxSignalNotifications( 
    DEVICE_EXTENSION *pdx, 
    U32               IntSource 
    ) 
{ 
    PLIST_ENTRY      pEntry; 
    PLX_WAIT_OBJECT *pWaitObject; 
 
 
    KeAcquireSpinLockAtDpcLevel( 
        &(pdx->Lock_WaitObjectsList) 
        ); 
 
    // Get the interrupt wait list 
    pEntry = pdx->List_WaitObjects.Flink; 
 
    // Traverse wait objects and wake-up processes 
    while (pEntry != &(pdx->List_WaitObjects)) 
    { 
        // Get the wait object 
        pWaitObject = 
            CONTAINING_RECORD( 
                pEntry, 
                PLX_WAIT_OBJECT, 
                ListEntry 
                ); 
 
        // Check if waiting for active interrupt 
        if (pWaitObject->NotifyOnInterrupt & IntSource) 
        { 
            DebugPrintf(( 
                "DPC signaling wait object (%p)\n", 
                pWaitObject 
                )); 
 
            // Save the new interrupt source in case later requested 
            pWaitObject->IntSource |= pWaitObject->NotifyOnInterrupt & IntSource; 
 
            // Signal wait object 
            KeSetEvent( 
                pWaitObject->pKEvent, 
                IO_NO_INCREMENT, 
                FALSE 
                ); 
        } 
 
        // Jump to next item in the list 
        pEntry = pEntry->Flink; 
    } 
 
    KeReleaseSpinLockFromDpcLevel( 
        &(pdx->Lock_WaitObjectsList) 
        ); 
} 
 
 
 
 
/********************************************************************* 
 * 
 * Function   :  PlxRegistryInformationGet 
 * 
 * Description:  Gets driver configuration information from the registry 
 * 
 ********************************************************************/ 
VOID 
PlxRegistryInformationGet( 
    UNICODE_STRING       *pRegistryPath, 
    REGISTRY_INFORMATION *pRegistryInfo 
    ) 
{ 
    NTSTATUS                  status; 
    RTL_QUERY_REGISTRY_TABLE  RegTable[2]; 
#if defined(PLX_NT_DRIVER) 
    U32                      *marker; 
    WCHAR                     slBuffer[200]; 
    UNICODE_STRING            supportedList; 
    UNICODE_STRING            fragment; 
#endif 
 
 
    RtlZeroMemory( 
        RegTable, 
        sizeof(RegTable) 
        ); 
 
    RegTable[0].Flags = RTL_QUERY_REGISTRY_REQUIRED | RTL_QUERY_REGISTRY_DIRECT; 
 
 
    // Get Common DMA buffer size 
    RegTable[0].Name         = L"CommonBufferSize"; 
    RegTable[0].EntryContext = &(pRegistryInfo->CommonBufferSize); 
 
    status = 
        RtlQueryRegistryValues( 
            RTL_REGISTRY_ABSOLUTE, 
            pRegistryPath->Buffer, 
            RegTable, 
            NULL, 
            NULL 
            ); 
 
    if ( !NT_SUCCESS(status) ) 
    { 
        pRegistryInfo->CommonBufferSize = DEFAULT_SIZE_COMMON_BUFFER; 
    } 
 
    // Get cacheability of physical memory buffers 
    RegTable[0].Name         = L"PhysicalMemoryCacheable"; 
    RegTable[0].EntryContext = &(pRegistryInfo->PhysicalMemoryCacheable); 
 
    status = 
        RtlQueryRegistryValues( 
            RTL_REGISTRY_ABSOLUTE, 
            pRegistryPath->Buffer, 
            RegTable, 
            NULL, 
            NULL 
            ); 
 
    if ( !NT_SUCCESS(status) ) 
    { 
        pRegistryInfo->PhysicalMemoryCacheable = DEFAULT_PHYS_MEM_CACHE_ENABLED; 
    } 
    else 
    { 
        if (pRegistryInfo->PhysicalMemoryCacheable == 0) 
        { 
            pRegistryInfo->PhysicalMemoryCacheable = FALSE; 
        } 
        else 
        { 
            pRegistryInfo->PhysicalMemoryCacheable = TRUE; 
        } 
    } 
 
 
#if defined(PLX_NT_DRIVER) 
    // Get list of Valid IDs 
    supportedList.MaximumLength = sizeof(slBuffer); 
    supportedList.Buffer        = slBuffer; 
 
    RegTable[0].Name          = L"SupportedIDs"; 
    RegTable[0].EntryContext  = &supportedList; 
 
    status = 
        RtlQueryRegistryValues( 
            RTL_REGISTRY_ABSOLUTE, 
            pRegistryPath->Buffer, 
            RegTable, 
            NULL, 
            NULL 
            ); 
 
    if ( !NT_SUCCESS(status) ) 
    { 
        DebugPrintf(( 
            "ERROR - Unable to read %ws registry entry\n", 
            RegTable[0].Name 
            )); 
 
        pRegistryInfo->ValidIdList[0] = PLX_DEFAULT_DEV_VEN_ID; 
        pRegistryInfo->IdListSize     = 1; 
    } 
    else 
    { 
        // Now parse the unicode list and place the ID values into the array 
 
        marker                    = pRegistryInfo->ValidIdList; 
        pRegistryInfo->IdListSize = 0; 
        fragment.Buffer           = supportedList.Buffer; 
        fragment.MaximumLength    = sizeof(WCHAR) * 8; 
        fragment.Length           = sizeof(WCHAR) * 8; 
 
        while (1) 
        { 
            RtlUnicodeStringToInteger( 
                &fragment, 
                0x10, 
                marker 
                ); 
 
            fragment.Buffer += 9; /* must skip to next value +1 for space */ 
            marker++; 
            (pRegistryInfo->IdListSize) += 1; 
 
            if ((U32)fragment.Buffer >= (U32)supportedList.Buffer + (U32)supportedList.Length) 
                break; 
        } 
    } 
#endif 
} 
 
 
 
 
/****************************************************************************** 
 * 
 * Function   :  GetBarIndex 
 * 
 * Description:  Associate a h/w resource with a bar number 
 * 
 ******************************************************************************/ 
U8 
GetBarIndex( 
    PHYSICAL_ADDRESS   address, 
    PCI_COMMON_CONFIG *pPciRegs 
    ) 
{ 
    U8  i; 
    U32 CompareAddress; 
 
 
    // Compare the physical address with each BAR 
    for (i = 0; i < PCI_NUM_BARS_TYPE_00; i++) 
    { 
        if (pPciRegs->u.type0.BaseAddresses[i] & 0x1) 
            CompareAddress = pPciRegs->u.type0.BaseAddresses[i] & 0xFFFFFFFC; 
        else 
            CompareAddress = pPciRegs->u.type0.BaseAddresses[i] & 0xFFFFFFF0; 
 
        if (address.u.LowPart == CompareAddress) 
            return i; 
    } 
 
    // Unable to find the BAR index 
    DebugPrintf(( 
        "ERROR - GetBarIndex() unable to match BAR value (0x%08lx)\n", 
        address 
        )); 
 
    return (U8)-1; 
} 
 
 
 
 
/****************************************************************************** 
 * 
 * Function   :  PlxCompleteIrp 
 * 
 * Description:  Complete an IRP 
 * 
 ******************************************************************************/ 
NTSTATUS 
PlxCompleteIrp( 
    PIRP     pIrp, 
    NTSTATUS status 
    ) 
{ 
    pIrp->IoStatus.Status = status; 
 
    IoCompleteRequest( 
        pIrp, 
        IO_NO_INCREMENT 
        ); 
 
    if (status == STATUS_CANCELLED) 
        DebugPrintf(("...Cancelled IRP (0x%p)\n", pIrp)); 
    else 
        DebugPrintf(("...Completed IRP (0x%p)\n", pIrp)); 
 
    return status; 
} 
 
 
 
 
/****************************************************************************** 
 * 
 * Function   :  PlxCompleteIrpWithInformation 
 * 
 * Description:  Complete an IRP including modification of the Information field 
 * 
 ******************************************************************************/ 
NTSTATUS 
PlxCompleteIrpWithInformation( 
    PIRP         pIrp, 
    NTSTATUS     status, 
    PLX_UINT_PTR Info 
    ) 
{ 
    pIrp->IoStatus.Status      = status; 
    pIrp->IoStatus.Information = Info; 
 
    IoCompleteRequest( 
        pIrp, 
        IO_NO_INCREMENT 
        ); 
 
    if (status == STATUS_CANCELLED) 
        DebugPrintf(("...Cancelled IRP (0x%p)\n", pIrp)); 
    else 
        DebugPrintf(("...Completed IRP (0x%p)\n", pIrp)); 
 
    return status; 
} 
 
 
 
 
/****************************************************************************** 
 * 
 * Function   :  Plx_sleep 
 * 
 * Description:  Function as a normal sleep. Parameter is in millisecond 
 * 
 ******************************************************************************/ 
VOID 
Plx_sleep( 
    U32 delay 
    ) 
{ 
    LARGE_INTEGER liTime; 
 
 
    /* Convert milliseconds to 100-nanosecond increments using: 
     * 
     *     1 ns  = 10 ^ -9 sec 
     *   100 ns  = 10 ^ -7 sec (1 timer interval) 
     *     1 ms  = 10 ^ -3 sec 
     *     1 ms  = (1 timer interval) * 10^4 
     */ 
    delay = delay * 10000; 
 
    // Negative value means relative time, not absolute 
    liTime = 
        RtlConvertLongToLargeInteger( 
            -(LONG)delay 
            ); 
 
    KeDelayExecutionThread( 
        KernelMode, 
        TRUE, 
        &liTime 
        ); 
} 
 
 
 
 
/****************************************************************************** 
 * 
 * Function   :  PlxPciBarResourceMap 
 * 
 * Description:  Maps a PCI BAR resource into kernel space 
 * 
 ******************************************************************************/ 
NTSTATUS 
PlxPciBarResourceMap( 
    DEVICE_EXTENSION *pdx, 
    U8                BarIndex 
    ) 
{ 
    U32   SizeToMap; 
    U32   SizeRemain; 
    VOID *pVa; 
    PMDL  pMdl; 
    PMDL  pMdl_Previous; 
 
 
    // Map into Kernel Virtual Space 
    pdx->PciBar[BarIndex].pVa = 
        MmMapIoSpace( 
            pdx->PciBar[BarIndex].Physical, 
            pdx->PciBar[BarIndex].Size, 
            MmNonCached 
            ); 
 
    if (pdx->PciBar[BarIndex].pVa == NULL) 
    { 
        return STATUS_INSUFFICIENT_RESOURCES; 
    } 
 
    /****************************************************************** 
     * A future mapping into user space will require an MDL.  Although 
     * it is not documented in the Windows DDK, a single MDL is limited 
     * to describing just under 64MB.  This is because IoAllocateMdl 
     * only allocates 64k for the MDL, which includes space for the 
     * physical page addresses that follow the MDL. 
     * 
     * In that case, we must allocate multiple MDLs and connect them 
     * by using the "Next" field of the MDL. 
     *****************************************************************/ 
 
    // Set initial values 
    pVa        = pdx->PciBar[BarIndex].pVa; 
    SizeRemain = pdx->PciBar[BarIndex].Size; 
 
    while (SizeRemain != 0) 
    { 
        // Determine size for MDL 
        if (SizeRemain <= MAX_MDL_SIZE) 
        { 
            SizeToMap = SizeRemain; 
        } 
        else 
        { 
            SizeToMap = MAX_MDL_SIZE; 
        } 
 
        // Get an MDL for future mapping into user space 
        pMdl = 
            IoAllocateMdl( 
                pVa, 
                SizeToMap, 
                FALSE, 
                FALSE, 
                NULL 
                ); 
 
        // Check if the MDL allocation succeeded 
        if (pMdl == NULL) 
        { 
            DebugPrintf(("ERROR - MDL allocation for space failed\n")); 
 
            // Release any created MDLs 
            while (pdx->PciBar[BarIndex].pMdl != NULL) 
            { 
                pMdl = pdx->PciBar[BarIndex].pMdl; 
 
                pdx->PciBar[BarIndex].pMdl = pMdl->Next; 
 
                IoFreeMdl( 
                    pMdl 
                    ); 
            } 
 
            return STATUS_SUCCESS; 
        } 
        else 
        { 
            // Build the MDL 
            MmBuildMdlForNonPagedPool( 
                pMdl 
                ); 
        } 
 
        // Clear next MDL pointer 
        pMdl->Next = NULL; 
 
        // Store MDL 
        if (pdx->PciBar[BarIndex].pMdl == NULL) 
        { 
            // Insert first MDL 
            pdx->PciBar[BarIndex].pMdl = pMdl; 
 
            pMdl_Previous = pMdl; 
        } 
        else 
        { 
            // Add new MDL to list 
            pMdl_Previous->Next = pMdl; 
 
            // Update previous pointer 
            pMdl_Previous = pMdl; 
        } 
 
        // Adjust for next virtual address 
        (U8*)pVa += SizeToMap; 
 
        // Adjust remaining count 
        SizeRemain -= SizeToMap; 
    } 
 
    return STATUS_SUCCESS; 
} 
 
 
 
 
/****************************************************************************** 
 * 
 * Function   :  PlxPciBarResourcesUnmap 
 * 
 * Description:  Unmap all mapped PCI BAR memory for a device 
 * 
 ******************************************************************************/ 
VOID 
PlxPciBarResourcesUnmap( 
    DEVICE_EXTENSION *pdx 
    ) 
{ 
    U8   i; 
    PMDL pMdl; 
 
 
    // Go through all the BARS 
    for (i = 0; i < PCI_NUM_BARS_TYPE_00; i++) 
    { 
        // Unmap the space from Kernel space if previously mapped 
        if ((pdx->PciBar[i].bIsIoSpace == FALSE) && 
            (pdx->PciBar[i].Physical.QuadPart != 0)) 
        { 
            DebugPrintf(("Unmapping BAR %d...\n", i)); 
 
            // Release the MDLs describing the BAR space 
            while (pdx->PciBar[i].pMdl != NULL) 
            { 
                pMdl = pdx->PciBar[i].pMdl; 
 
                pdx->PciBar[i].pMdl = pMdl->Next; 
 
                IoFreeMdl( 
                    pMdl 
                    ); 
            } 
 
            // Unmap from kernel space 
            if (pdx->PciBar[i].pVa != NULL) 
            { 
                MmUnmapIoSpace( 
                    pdx->PciBar[i].pVa, 
                    pdx->PciBar[i].Size 
                    ); 
                pdx->PciBar[i].pVa = NULL; 
            } 
        } 
    } 
} 
 
 
 
 
/****************************************************************************** 
 * 
 * Function   :  PlxPciBarSpaceUnmapAll_ByOwner 
 * 
 * Description:  Unmap any PCI BAR spaces assigned to the specified owner 
 * 
 ******************************************************************************/ 
VOID 
PlxPciBarSpaceUnmapAll_ByOwner( 
    DEVICE_EXTENSION *pdx, 
    VOID             *pOwner 
    ) 
{ 
    VOID             *UserVa; 
    KIRQL             IrqL_Original; 
    PLIST_ENTRY       pEntry; 
    PLX_USER_MAPPING *pMapObject; 
 
 
    KeAcquireSpinLock( 
        &(pdx->Lock_BarMappingsList), 
        &IrqL_Original 
        ); 
 
    pEntry = pdx->List_BarMappings.Flink; 
 
    // Traverse list to find the desired list objects 
    while (pEntry != &(pdx->List_BarMappings)) 
    { 
        // Get the object 
        pMapObject = 
            CONTAINING_RECORD( 
                pEntry, 
                PLX_USER_MAPPING, 
                ListEntry 
                ); 
 
        // Check if owner matches 
        if (pMapObject->pOwner == pOwner) 
        { 
            // Copy address 
            UserVa = pMapObject->pUserVa[0]; 
 
            // Release list lock 
            KeReleaseSpinLock( 
                &(pdx->Lock_BarMappingsList), 
                IrqL_Original 
                ); 
 
            // Unmap BAR space 
            PlxPciBarUnmap( 
                pdx, 
                UserVa, 
                pOwner 
                );  
 
            KeAcquireSpinLock( 
                &(pdx->Lock_BarMappingsList), 
                &IrqL_Original 
                ); 
 
            // Restart parsing the list from the beginning 
            pEntry = pdx->List_BarMappings.Flink; 
        } 
        else 
        { 
            // Jump to next item 
            pEntry = pEntry->Flink; 
        } 
    } 
 
    KeReleaseSpinLock( 
        &(pdx->Lock_BarMappingsList), 
        IrqL_Original 
        ); 
} 
 
 
 
 
/****************************************************************************** 
 * 
 * Function   :  PlxPciPhysicalMemoryFreeAll_ByOwner 
 * 
 * Description:  Unmap & release all physical memory assigned to the specified owner 
 * 
 ******************************************************************************/ 
VOID 
PlxPciPhysicalMemoryFreeAll_ByOwner( 
    DEVICE_EXTENSION *pdx, 
    VOID             *pOwner 
    ) 
{ 
    KIRQL                IrqL_Original; 
    PLIST_ENTRY          pEntry; 
    PLX_PHYSICAL_MEM     PciMem; 
    PLX_PHYS_MEM_OBJECT *pMemObject; 
 
 
    KeAcquireSpinLock( 
        &(pdx->Lock_PhysicalMemList), 
        &IrqL_Original 
        ); 
 
    pEntry = pdx->List_PhysicalMem.Flink; 
 
    // Traverse list to find the desired list objects 
    while (pEntry != &(pdx->List_PhysicalMem)) 
    { 
        // Get the object 
        pMemObject = 
            CONTAINING_RECORD( 
                pEntry, 
                PLX_PHYS_MEM_OBJECT, 
                ListEntry 
                ); 
 
        // Check if owner matches 
        if (pMemObject->pOwner == pOwner) 
        { 
            // Copy memory information 
            PciMem.PhysicalAddr = pMemObject->BusPhysical; 
            PciMem.Size         = pMemObject->Size; 
 
            // Release list lock 
            KeReleaseSpinLock( 
                &(pdx->Lock_PhysicalMemList), 
                IrqL_Original 
                ); 
 
            // Release the memory & remove from list 
            PlxPciPhysicalMemoryFree( 
                pdx, 
                &PciMem 
                );  
 
            KeAcquireSpinLock( 
                &(pdx->Lock_PhysicalMemList), 
                &IrqL_Original 
                ); 
 
            // Restart parsing the list from the beginning 
            pEntry = pdx->List_PhysicalMem.Flink; 
        } 
        else 
        { 
            // Jump to next item 
            pEntry = pEntry->Flink; 
        } 
    } 
 
    KeReleaseSpinLock( 
        &(pdx->Lock_PhysicalMemList), 
        IrqL_Original 
        ); 
} 
 
 
 
 
/****************************************************************************** 
 * 
 * Function   :  PlxPciPhysicalMemoryUnmapAll_ByOwner 
 * 
 * Description:  Unmap any physical memory assigned to the specified owner 
 * 
 ******************************************************************************/ 
VOID 
PlxPciPhysicalMemoryUnmapAll_ByOwner( 
    DEVICE_EXTENSION    *pdx, 
    PLX_PHYS_MEM_OBJECT *pMemObject, 
    VOID                *pOwner 
    ) 
{ 
    VOID             *pUserVa; 
    KIRQL             IrqL_Original; 
    PLIST_ENTRY       pEntry; 
    PLX_PHYSICAL_MEM  PciMem; 
    PHYSICAL_ADDRESS  AddrPhysical; 
    PLX_USER_MAPPING *pMapObject; 
 
 
    // Verify Memory Object 
    if (pMemObject == NULL) 
    { 
        return; 
    } 
 
    // Verify size 
    if (pMemObject->Size == 0) 
    { 
        return; 
    } 
 
    // Setup memory properties 
    PciMem.PhysicalAddr = pMemObject->BusPhysical; 
    PciMem.Size         = pMemObject->Size; 
 
    // Find the map object to unmap 
    KeAcquireSpinLock( 
        &(pMemObject->Lock_MappingsList), 
        &IrqL_Original 
        ); 
 
    pEntry = pMemObject->List_Mappings.Flink; 
 
    // Traverse list to find the desired list object 
    while (pEntry != &(pMemObject->List_Mappings)) 
    { 
        // Get the object 
        pMapObject = 
            CONTAINING_RECORD( 
                pEntry, 
                PLX_USER_MAPPING, 
                ListEntry 
                ); 
 
        // Check if the owner matches 
        if ((pOwner == NULL) || (pMapObject->pOwner == pOwner)) 
        { 
            // Copy virtual address 
            PciMem.UserAddr = (PLX_UINT_PTR)pMapObject->pUserVa[0]; 
 
            // Release list lock 
            KeReleaseSpinLock( 
                &(pMemObject->Lock_MappingsList), 
                IrqL_Original 
                ); 
 
            // Unmap the memory & remove from list 
            PlxPciPhysicalMemoryUnmap( 
                pdx, 
                &PciMem, 
                pMapObject->pOwner 
                );  
 
            KeAcquireSpinLock( 
                &(pMemObject->Lock_MappingsList), 
                &IrqL_Original 
                ); 
 
            // Restart parsing the list from the beginning 
            pEntry = pMemObject->List_Mappings.Flink; 
        } 
        else 
        { 
            // Jump to next item 
            pEntry = pEntry->Flink; 
        } 
    } 
 
    KeReleaseSpinLock( 
        &(pMemObject->Lock_MappingsList), 
        IrqL_Original 
        ); 
} 
 
 
 
 
/****************************************************************************** 
 * 
 * Function   :  PlxDmaChannelCleanup 
 * 
 * Description:  Called by the Cleanup routine to close any open channels 
 * 
 ******************************************************************************/ 
VOID 
PlxDmaChannelCleanup( 
    DEVICE_EXTENSION *pdx, 
    VOID             *pOwner 
    ) 
{ 
#if defined(DMA_SUPPORT) 
    U8          i; 
    DMA_CHANNEL channel; 
 
 
    // Added to avoid compiler warning 
    channel = PrimaryPciChannel0; 
 
    for (i = 0; i < NUMBER_OF_DMA_CHANNELS; i++) 
    { 
        // Check if terminating application is the owner 
        if (pdx->DmaInfo[i].pOwner == pOwner) 
        { 
            DebugPrintf(("Closing DMA channel %d\n", i)); 
 
            switch (i) 
            { 
                case 0: 
                    channel = PrimaryPciChannel0; 
                    break; 
 
                case 1: 
                    channel = PrimaryPciChannel1; 
                    break; 
 
                case 2: 
                    channel = IopChannel2; 
                    break; 
            } 
 
            switch (pdx->DmaInfo[i].state) 
            { 
                case DmaStateClosed: 
                    // DMA closed already, do nothing 
                    break; 
 
                case DmaStateBlock: 
                    PlxDmaBlockChannelClose( 
                        pdx, 
                        channel, 
                        FALSE 
                        ); 
                    break; 
 
                case DmaStateSgl: 
                    PlxDmaSglChannelClose( 
                        pdx, 
                        channel, 
                        FALSE 
                        ); 
                    break; 
 
                default: 
                    // Unknown or unsupported state 
                    break; 
            } 
        } 
    } 
#endif  // DMA_SUPPORT 
} 
 
 
 
 
#if defined(DMA_SUPPORT) 
/****************************************************************************** 
 * 
 * Function   :  PlxSglDmaTransferComplete 
 * 
 * Description:  Perform any necessary cleanup after an SGL DMA transfer 
 * 
 ******************************************************************************/ 
VOID 
PlxSglDmaTransferComplete( 
    DEVICE_EXTENSION *pdx, 
    U8                DmaIndex 
    ) 
{ 
    KIRQL IrqL_Original; 
 
 
    if (pdx->DmaInfo[DmaIndex].bPending == FALSE) 
    { 
        DebugPrintf(("No pending SGL DMA to complete\n")); 
        return; 
    } 
 
    DebugPrintf(( 
        "Unlocking user-mode buffer used for SGL DMA transfer...\n" 
        )); 
 
    KeAcquireSpinLock( 
        &(pdx->Lock_DmaChannel), 
        &IrqL_Original 
        ); 
 
    // Unlock the data buffer and free the MDL 
    if (pdx->DmaInfo[DmaIndex].pMdl != NULL) 
    { 
        MmUnlockPages( 
            pdx->DmaInfo[DmaIndex].pMdl 
            ); 
 
        IoFreeMdl( 
            pdx->DmaInfo[DmaIndex].pMdl 
            ); 
 
        pdx->DmaInfo[DmaIndex].pMdl = NULL; 
    } 
 
    // Clear the DMA pending flag 
    pdx->DmaInfo[DmaIndex].bPending = FALSE; 
 
    KeReleaseSpinLock( 
        &(pdx->Lock_DmaChannel), 
        IrqL_Original 
        ); 
} 
 
 
 
 
/****************************************************************************** 
 * 
 * Function   :  PlxLockBufferAndBuildSgl 
 * 
 * Description:  Lock a user buffer and build an SGL for it 
 * 
 ******************************************************************************/ 
U32 
PlxLockBufferAndBuildSgl( 
    DEVICE_EXTENSION     *pdx, 
    U8                    DmaIndex, 
    DMA_TRANSFER_ELEMENT *pDma 
    ) 
{ 
    U32           i; 
    U32           offset; 
    U32           BusSgl; 
    U32           BusSglOriginal; 
    U32           BusAddr; 
    U32           SglSize; 
    U32           BlockSize; 
    U32           LocalAddr; 
    U32           TotalPages; 
    U32          *pCurrentPage; 
    U32           BytesRemaining; 
    PMDL          pMdl; 
    KIRQL         IrqL_Original; 
    BOOLEAN       bFlag; 
    PLX_UINT_PTR  VaSgl; 
 
 
    DebugPrintf(("Building SGL descriptors for buffer...\n")); 
    DebugPrintf(("   User VA   : 0x%08lx\n", (PLX_UINT_PTR)pDma->u.UserVa)); 
    DebugPrintf(("   Local Addr: 0x%08x\n", pDma->LocalAddr)); 
    DebugPrintf(("   Size      : %d bytes\n", pDma->TransferCount)); 
 
    // Get the MDL for the User buffer to get the page list 
    pMdl = 
        IoAllocateMdl( 
            (VOID*)pDma->u.UserVa, 
            pDma->TransferCount, 
            FALSE, 
            FALSE, 
            NULL 
            ); 
 
    if (pMdl == NULL) 
    { 
        DebugPrintf(("ERROR - Unable to allocate an MDL for the User buffer\n")); 
        return 0; 
    } 
 
    // Attempt to lock the user buffer into memory 
    bFlag = FALSE; 
 
    try 
    { 
        MmProbeAndLockPages( 
            pMdl, 
            UserMode, 
            IoModifyAccess 
            ); 
    } 
    except(EXCEPTION_EXECUTE_HANDLER) 
    { 
        // Flag the exception 
        bFlag = TRUE; 
    } 
 
    // Verify that the pages were locked 
    if (bFlag) 
    { 
        DebugPrintf(("ERROR - Unable to lock user mode buffer pages\n")); 
        IoFreeMdl( 
            pMdl 
            ); 
        return 0; 
    } 
 
    // Calculate total pages 
    TotalPages  = (((U16)pMdl->Size - sizeof(MDL)) / sizeof(U32)); 
 
    DebugPrintf(( 
        "Page-locked %d user buffer pages...\n", 
        TotalPages 
        )); 
 
 
    /****************************************************** 
     * Build SGL descriptors 
     * 
     * The following code will build the SGL descriptors 
     * in PCI memory.  There will be one descriptor for 
     * each page of memory since the pages are scattered 
     * throughout physical memory. 
     *****************************************************/ 
 
    /************************************************************* 
     * Calculate memory needed for SGL descriptors 
     * 
     * Mem needed = #pages * size of descriptor 
     * 
     * Additionally, a value of 16 (10h) is added to provide a 
     * buffer to allow space to round up to the next 16-byte 
     * boundary, which is a requirement of the hardware. 
     ************************************************************/ 
    SglSize = (TotalPages * (4 * sizeof(U32))) + 16; 
 
    // Check if a previously allocated buffer can be re-used 
    if (pdx->DmaInfo[DmaIndex].SglBuffer.pKernelVa != NULL) 
    { 
        if (pdx->DmaInfo[DmaIndex].SglBuffer.Size >= SglSize) 
        { 
            // Buffer can be re-used, do nothing 
            DebugPrintf(("Re-using previously allocated SGL descriptor buffer\n")); 
        } 
        else 
        { 
            DebugPrintf(("Releasing previously allocated SGL descriptor buffer\n")); 
 
            // Release memory used for SGL descriptors 
            Plx_dma_buffer_free( 
                pdx, 
                &pdx->DmaInfo[DmaIndex].SglBuffer 
                ); 
 
            pdx->DmaInfo[DmaIndex].SglBuffer.pKernelVa = NULL; 
        } 
    } 
 
    // Allocate memory for SGL descriptors if necessary 
    if (pdx->DmaInfo[DmaIndex].SglBuffer.pKernelVa == NULL) 
    { 
        DebugPrintf(("Allocating PCI memory for SGL descriptor buffer...\n")); 
 
        // Setup for transfer 
        pdx->DmaInfo[DmaIndex].SglBuffer.Size       = SglSize; 
        pdx->DmaInfo[DmaIndex].SglBuffer.bCacheable = FALSE; 
 
        VaSgl = 
            (PLX_UINT_PTR)Plx_dma_buffer_alloc( 
                pdx, 
                &pdx->DmaInfo[DmaIndex].SglBuffer 
                ); 
 
        if (VaSgl == 0) 
        { 
            DebugPrintf(( 
                "ERROR - Unable to allocate %d bytes for %d SGL descriptors\n", 
                pdx->DmaInfo[DmaIndex].SglBuffer.Size, 
                TotalPages 
                )); 
 
            MmUnlockPages( 
                pMdl 
                ); 
 
            IoFreeMdl( 
                pMdl 
                ); 
 
            return 0; 
        } 
    } 
    else 
    { 
        VaSgl = (PLX_UINT_PTR)pdx->DmaInfo[DmaIndex].SglBuffer.pKernelVa; 
    } 
 
 
    // Save MDL for deallocation after the DMA transfer 
    KeAcquireSpinLock( 
        &(pdx->Lock_DmaChannel), 
        &IrqL_Original 
        ); 
 
    pdx->DmaInfo[DmaIndex].pMdl = pMdl; 
 
    KeReleaseSpinLock( 
        &(pdx->Lock_DmaChannel), 
        IrqL_Original 
        ); 
 
    // Prepare for build of SGL 
    LocalAddr = pDma->LocalAddr; 
 
    // Get bus physical address of SGL descriptors 
    BusSgl = (U32)pdx->DmaInfo[DmaIndex].SglBuffer.BusPhysical; 
 
    // Make sure addresses are aligned on 16-byte boundary 
    VaSgl  = (VaSgl + 0xF) & ~(0xF); 
    BusSgl = (BusSgl + 0xF) & ~(0xF); 
 
    // Store the starting address of the SGL for later return 
    BusSglOriginal = BusSgl; 
 
    DebugPrintf(( 
        "Building SGL descriptor list located at 0x%08x...\n", 
        BusSglOriginal 
        )); 
 
    // Point to first page in physical page list (located at end of MDL) 
    pCurrentPage = (U32*)(((U8*)pMdl) + sizeof(MDL)); 
 
    // Set offset of first page 
    offset = pMdl->ByteOffset; 
 
    // Initialize bytes remaining 
    BytesRemaining = pDma->TransferCount; 
 
    // Build the SGL list 
    for (i = 0; i < TotalPages; i++) 
    { 
        // Calculate transfer size 
        if (BytesRemaining > (PAGE_SIZE - offset)) 
        { 
            BlockSize = PAGE_SIZE - offset; 
        } 
        else 
        { 
            BlockSize = BytesRemaining; 
        } 
 
        // Get bus address of buffer 
        BusAddr = (*pCurrentPage << PAGE_SHIFT) + offset; 
 
        // Enable the following to display the parameters of each SGL descriptor 
        if (PLX_DEBUG_DISPLAY_SGL_DESCR) 
        { 
            DebugPrintf(( 
                "SGL Desc %02d: PCI=%08Xh  Loc=%08Xh  Size=%Xh (%d) bytes\n", 
                i, (U32)BusAddr, LocalAddr, BlockSize, BlockSize 
                )); 
        } 
 
        // Write PCI address in descriptor 
        *(((U32*)VaSgl) + SGL_DESC_IDX_PCI_ADDR) = BusAddr; 
 
        // Write Local address in descriptor 
        *(((U32*)VaSgl) + SGL_DESC_IDX_LOC_ADDR) = LocalAddr; 
 
        // Write transfer count in descriptor 
        *(((U32*)VaSgl) + SGL_DESC_IDX_COUNT) = BlockSize; 
 
        // Adjust byte count 
        BytesRemaining -= BlockSize; 
 
        if (BytesRemaining == 0) 
        { 
            // Write the last descriptor 
            *(((U32*)VaSgl) + SGL_DESC_IDX_NEXT_DESC) = 
                            (pDma->LocalToPciDma << 3) | (1 << 1) | (1 << 0); 
        } 
        else 
        { 
            // Calculate address of next descriptor 
            BusSgl += (4 * sizeof(U32)); 
 
            // Write next descriptor address 
            *(((U32*)VaSgl) + SGL_DESC_IDX_NEXT_DESC) = 
                             BusSgl | (pDma->LocalToPciDma << 3) | (1 << 0); 
 
            // Adjust Local address 
            if (pdx->DmaInfo[DmaIndex].bLocalAddrConstant == FALSE) 
                LocalAddr += BlockSize; 
 
            // Adjust virtual address to next descriptor 
            VaSgl += (4 * sizeof(U32)); 
 
            // Clear offset 
            offset = 0; 
        } 
 
        // Go to next page 
        pCurrentPage++; 
    } 
 
    // Make sure cache data is flushed 
    KeFlushIoBuffers( 
        pMdl, 
        (BOOLEAN)pDma->LocalToPciDma,   // Read from device operation? 
        TRUE                            // DMA transfer? 
        ); 
 
    // Return the physical address of the SGL 
    return BusSglOriginal; 
} 
 
#endif // DMA_SUPPORT 
 
 
 
 
/****************************************************************************** 
 * 
 * Function   :  Plx_dma_buffer_alloc 
 * 
 * Description:  Allocates physically contiguous non-paged memory 
 * 
 ******************************************************************************/ 
VOID* 
Plx_dma_buffer_alloc( 
    DEVICE_EXTENSION    *pdx, 
    PLX_PHYS_MEM_OBJECT *pMemObject 
    ) 
{ 
    PHYSICAL_ADDRESS BufferLogicalAddress; 
 
 
    // Verify the DMA adapter object 
    if (pdx->pDmaAdapter == NULL) 
    { 
        DebugPrintf(( 
            "ERROR - DMA Adapter object does not exist, cannot allocate physical memory\n" 
            )); 
 
        return NULL; 
    } 
 
    // Attempt to allocate contiguous memory 
#if defined(PLX_NT_DRIVER) 
    pMemObject->pKernelVa = 
        HalAllocateCommonBuffer( 
            pdx->pDmaAdapter, 
            pMemObject->Size, 
            &BufferLogicalAddress, 
            pMemObject->bCacheable      // Enable Caching for buffer? 
            ); 
#else 
    pMemObject->pKernelVa = 
        pdx->pDmaAdapter->DmaOperations->AllocateCommonBuffer( 
            pdx->pDmaAdapter, 
            pMemObject->Size, 
            &BufferLogicalAddress, 
            pMemObject->bCacheable      // Enable Caching for buffer? 
            ); 
#endif // PLX_NT_DRIVER 
 
    if (pMemObject->pKernelVa == NULL) 
    { 
        return NULL; 
    } 
 
    // Store the buffer logical/bus physical address 
    pMemObject->BusPhysical = BufferLogicalAddress.QuadPart; 
 
    // CPU Address is the same as bus on this architecture 
    pMemObject->CpuPhysical = BufferLogicalAddress.QuadPart; 
 
    // Clear the buffer 
    RtlZeroMemory( 
        pMemObject->pKernelVa, 
        pMemObject->Size 
        ); 
 
    DebugPrintf(("Allocated physical memory...\n")); 
 
    DebugPrintf(( 
        "    CPU Phys Addr: 0x%08lx\n", 
        (PLX_UINT_PTR)pMemObject->CpuPhysical 
        )); 
 
    DebugPrintf(( 
        "    Bus Phys Addr: 0x%08lx\n", 
        (PLX_UINT_PTR)pMemObject->BusPhysical 
        )); 
 
    DebugPrintf(( 
        "    Kernel VA    : 0x%p\n", 
        pMemObject->pKernelVa 
        )); 
 
    DebugPrintf(( 
        "    Size         : " 
        )); 
 
    if (pMemObject->Size >= (10 << 10)) 
    { 
        DebugPrintf_NoInfo(( 
            "%d Kb\n", 
            (pMemObject->Size >> 10) 
            )); 
    } 
    else 
    { 
        DebugPrintf_NoInfo(( 
            "%d bytes\n", 
            pMemObject->Size 
            )); 
    } 
 
    DebugPrintf(( 
        "    Cacheable?   : %s\n", 
        (pMemObject->bCacheable == FALSE) ? "No" : "Yes" 
        )); 
 
    return pMemObject->pKernelVa; 
} 
 
 
 
 
/****************************************************************************** 
 * 
 * Function   :  Plx_dma_buffer_free 
 * 
 * Description:  Frees previously allocated physical memory 
 * 
 ******************************************************************************/ 
VOID 
Plx_dma_buffer_free( 
    DEVICE_EXTENSION    *pdx, 
    PLX_PHYS_MEM_OBJECT *pMemObject 
    ) 
{ 
    PHYSICAL_ADDRESS  BufferLogicalAddress; 
 
 
    // Verify the DMA adapter object 
    if (pdx->pDmaAdapter == NULL) 
    { 
        DebugPrintf(( 
            "ERROR - DMA Adapter object does not exist, cannot allocate physical memory\n" 
            )); 
 
        return; 
    } 
 
    // Set logical address 
    BufferLogicalAddress.QuadPart = pMemObject->BusPhysical; 
 
    // Release the buffer 
 
#if defined(PLX_NT_DRIVER) 
    HalFreeCommonBuffer( 
        pdx->pDmaAdapter, 
        pMemObject->Size, 
        BufferLogicalAddress, 
        pMemObject->pKernelVa, 
        pMemObject->bCacheable      // Enable Caching for buffer? 
        ); 
#else 
    pdx->pDmaAdapter->DmaOperations->FreeCommonBuffer( 
        pdx->pDmaAdapter, 
        pMemObject->Size, 
        BufferLogicalAddress, 
        pMemObject->pKernelVa, 
        pMemObject->bCacheable      // Enable Caching for buffer? 
        ); 
#endif // PLX_NT_DRIVER 
 
    DebugPrintf(( 
        "Released physical memory at 0x%08lx ", 
        (PLX_UINT_PTR)pMemObject->CpuPhysical 
        )); 
 
    if (pMemObject->Size >= (10 << 10)) 
    { 
        DebugPrintf_NoInfo(( 
            "(%d Kb)\n", 
            (pMemObject->Size >> 10) 
            )); 
    } 
    else 
    { 
        DebugPrintf_NoInfo(( 
            "(%d bytes)\n", 
            pMemObject->Size 
            )); 
    } 
 
    // Clear memory object properties 
    RtlZeroMemory( 
        pMemObject, 
        sizeof(PLX_PHYS_MEM_OBJECT) 
        ); 
}