www.pudn.com > 9054Src.rar > ApiFunctions.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: 
 * 
 *      ApiFunctions.c 
 * 
 * Description: 
 * 
 *      This file contains the implementation of the PCI API functions 
 *      specific to a PLX Chip. 
 * 
 * Revision History: 
 * 
 *      03-01-06 : PCI SDK v4.40 
 * 
 ******************************************************************************/ 
 
 
#include "ApiFunctions.h" 
#include "Eeprom_9000.h" 
#include "PciSupport.h" 
#include "PlxInterrupt.h" 
#include "SupportFunc.h" 
 
 
 
 
/****************************************************************************** 
 * 
 * Function   :  PlxRegisterRead 
 * 
 * Description:  Reads a PLX chip local register 
 * 
 ******************************************************************************/ 
RETURN_CODE 
PlxRegisterRead( 
    DEVICE_EXTENSION *pdx, 
    U16               offset, 
    U32              *pValue 
    ) 
{ 
    // Make sure register offset is valid 
    if ((offset & 0x3) || (offset >= MAX_PLX_REG_OFFSET)) 
    { 
        DebugPrintf(("ERROR - invalid register offset (0x%x)\n", offset)); 
 
        *pValue = (U32)-1; 
 
        return ApiInvalidRegister; 
    } 
 
    *pValue = 
        PLX_REG_READ( 
            pdx, 
            offset 
            ); 
 
    return ApiSuccess; 
} 
 
 
 
 
/****************************************************************************** 
 * 
 * Function   :  PlxRegisterWrite 
 * 
 * Description:  Writes a value to a PLX local register 
 * 
 ******************************************************************************/ 
RETURN_CODE 
PlxRegisterWrite( 
    DEVICE_EXTENSION *pdx, 
    U16               offset, 
    U32               value 
    ) 
{ 
    // Make sure register offset is valid 
    if ((offset & 0x3) || (offset >= MAX_PLX_REG_OFFSET)) 
    { 
        DebugPrintf(("ERROR - invalid register offset (0x%x)\n", offset)); 
        return ApiInvalidRegister; 
    } 
 
    PLX_REG_WRITE( 
        pdx, 
        offset, 
        value 
        ); 
 
    return ApiSuccess; 
} 
 
 
 
 
/****************************************************************************** 
 * 
 * Function   :  PlxPciIntrEnable 
 * 
 * Description:  Enables specific interupts of the PLX Chip 
 * 
 ******************************************************************************/ 
RETURN_CODE 
PlxPciIntrEnable( 
    DEVICE_EXTENSION *pdx, 
    PLX_INTR         *pPlxIntr 
    ) 
{ 
    U32          QueueCsr; 
    U32          QueueCsr_Original; 
    U32          RegisterValue; 
    PLX_REG_DATA RegData; 
 
 
    // Setup to synchronize access to Interrupt Control/Status Register 
    RegData.pdx         = pdx; 
    RegData.offset      = PCI9054_INT_CTRL_STAT; 
    RegData.BitsToSet   = 0; 
    RegData.BitsToClear = 0; 
 
 
    // Inbound Post Queue Interrupt Control/Status Register 
    QueueCsr_Original = 
        PLX_REG_READ( 
            pdx, 
            PCI9054_FIFO_CTRL_STAT 
            ); 
 
    QueueCsr = QueueCsr_Original; 
 
 
    if (pPlxIntr->OutboundPost) 
    { 
        PLX_REG_WRITE( 
            pdx, 
            PCI9054_OUTPOST_INT_MASK, 
            0 
            ); 
    } 
 
    if (pPlxIntr->InboundPost) 
        QueueCsr &= ~(1 << 4); 
 
    if (pPlxIntr->OutboundOverflow) 
    { 
        RegData.BitsToSet |=  (1 << 1); 
        QueueCsr          &= ~(1 << 6); 
    } 
 
    if (pPlxIntr->PciDmaChannel0) 
    { 
        RegData.BitsToSet |= (1 << 18); 
 
        // Make sure DMA interrupt is routed to PCI 
        RegisterValue = 
            PLX_REG_READ( 
                pdx, 
                PCI9054_DMA0_MODE 
                ); 
 
        PLX_REG_WRITE( 
            pdx, 
            PCI9054_DMA0_MODE, 
            RegisterValue | (1 << 17) 
            ); 
    } 
 
    if (pPlxIntr->PciDmaChannel1) 
    { 
        RegData.BitsToSet |= (1 << 19); 
 
        // Make sure DMA interrupt is routed to PCI 
        RegisterValue = 
            PLX_REG_READ( 
                pdx, 
                PCI9054_DMA1_MODE 
                ); 
 
        PLX_REG_WRITE( 
            pdx, 
            PCI9054_DMA1_MODE, 
            RegisterValue | (1 << 17) 
            ); 
    } 
 
    if (pPlxIntr->IopDmaChannel0) 
    { 
        RegData.BitsToSet |= (1 << 18); 
 
        // Make sure DMA interrupt is routed to IOP 
        RegisterValue = 
            PLX_REG_READ( 
                pdx, 
                PCI9054_DMA0_MODE 
                ); 
 
        PLX_REG_WRITE( 
            pdx, 
            PCI9054_DMA0_MODE, 
            RegisterValue & ~(1 << 17) 
            ); 
    } 
 
    if (pPlxIntr->IopDmaChannel1) 
    { 
        RegData.BitsToSet |= (1 << 19); 
 
        // Make sure DMA interrupt is routed to IOP 
        RegisterValue = 
            PLX_REG_READ( 
                pdx, 
                PCI9054_DMA1_MODE 
                ); 
 
        PLX_REG_WRITE( 
            pdx, 
            PCI9054_DMA1_MODE, 
            RegisterValue & ~(1 << 17) 
            ); 
    } 
 
    if (pPlxIntr->Mailbox0 || pPlxIntr->Mailbox1 || 
        pPlxIntr->Mailbox2 || pPlxIntr->Mailbox3) 
    { 
        RegData.BitsToSet |= (1 << 3); 
    } 
 
    if (pPlxIntr->IopDoorbell) 
        RegData.BitsToSet |= (1 << 17); 
 
    if (pPlxIntr->PciDoorbell) 
        RegData.BitsToSet |= (1 << 9); 
 
    if (pPlxIntr->IopToPciInt) 
        RegData.BitsToSet |= (1 << 11); 
 
    if (pPlxIntr->PciMainInt) 
        RegData.BitsToSet |= (1 << 8); 
 
    if (pPlxIntr->IopMainInt) 
        RegData.BitsToSet |= (1 << 16); 
 
    if (pPlxIntr->PciAbort) 
        RegData.BitsToSet |= (1 << 10); 
 
    if (pPlxIntr->AbortLSERR) 
        RegData.BitsToSet |= (1 << 0); 
 
    if (pPlxIntr->ParityLSERR) 
        RegData.BitsToSet |= (1 << 1); 
 
    if (pPlxIntr->RetryAbort) 
        RegData.BitsToSet |= (1 << 12); 
 
    if (pPlxIntr->PowerManagement) 
        RegData.BitsToSet |= (1 << 4); 
 
    if (pPlxIntr->LocalParityLSERR) 
    { 
        RegData.BitsToSet |= (1 << 0); 
        RegData.BitsToSet |= (1 << 6); 
    } 
 
    // Write register values if they have changed 
 
    if (RegData.BitsToSet != 0) 
    { 
        // Synchronize write of Interrupt Control/Status Register 
        KeSynchronizeExecution( 
            pdx->pInterruptObject, 
            PlxSynchronizedRegisterModify, 
            &RegData 
            ); 
    } 
 
    if (QueueCsr != QueueCsr_Original) 
    { 
        PLX_REG_WRITE( 
            pdx, 
            PCI9054_FIFO_CTRL_STAT, 
            QueueCsr 
            ); 
    } 
 
    // Power Management Interrupt 
    if (pPlxIntr->PciPME) 
    { 
        // Verify that the capability is enabled 
        if (PlxIsNewCapabilityEnabled( 
                pdx, 
                CAPABILITY_POWER_MANAGEMENT 
                ) == TRUE) 
        { 
            PLX_PCI_REG_READ( 
                pdx, 
                PCI9054_PM_CSR, 
                &RegisterValue 
                ); 
 
            // Set PME Enable bit 
            RegisterValue |= (1 << 8); 
 
            PLX_PCI_REG_WRITE( 
                pdx, 
                PCI9054_PM_CSR, 
                RegisterValue 
                ); 
        } 
    } 
 
    // Hot Swap Interrupt 
    if (pPlxIntr->Enum) 
    { 
        // Verify that the capability is enabled 
        if (PlxIsNewCapabilityEnabled( 
                pdx, 
                CAPABILITY_HOT_SWAP 
                ) == TRUE) 
        { 
            PLX_PCI_REG_READ( 
                pdx, 
                PCI9054_HS_CAP_ID, 
                &RegisterValue 
                ); 
 
            // Turn off INS & EXT bits so we don't clear them 
            RegisterValue &= ~((1 << 23) | (1 << 22)); 
 
            // Disable Enum Interrupt mask 
            RegisterValue &= ~(1 << 17); 
 
            PLX_PCI_REG_WRITE( 
                pdx, 
                PCI9054_HS_CAP_ID, 
                RegisterValue 
                ); 
        } 
    } 
 
    return ApiSuccess; 
} 
 
 
 
 
/****************************************************************************** 
 * 
 * Function   :  PlxPciIntrDisable 
 * 
 * Description:  Disables specific interrupts of the PLX Chip 
 * 
 ******************************************************************************/ 
RETURN_CODE 
PlxPciIntrDisable( 
    DEVICE_EXTENSION *pdx, 
    PLX_INTR         *pPlxIntr 
    ) 
{ 
    U32          QueueCsr; 
    U32          QueueCsr_Original; 
    U32          RegisterValue; 
    PLX_REG_DATA RegData; 
 
 
    // Setup to synchronize access to Interrupt Control/Status Register 
    RegData.pdx         = pdx; 
    RegData.offset      = PCI9054_INT_CTRL_STAT; 
    RegData.BitsToSet   = 0; 
    RegData.BitsToClear = 0; 
 
    // Inbound Post Queue Interrupt Control/Status Register 
    QueueCsr_Original = 
        PLX_REG_READ( 
            pdx, 
            PCI9054_FIFO_CTRL_STAT 
            ); 
 
    QueueCsr = QueueCsr_Original; 
 
 
    if (pPlxIntr->OutboundPost) 
    { 
        PLX_REG_WRITE( 
            pdx, 
            PCI9054_OUTPOST_INT_MASK, 
            (1 << 3) 
            ); 
    } 
 
    if (pPlxIntr->InboundPost) 
        QueueCsr |= (1 << 4); 
 
    if (pPlxIntr->OutboundOverflow) 
        QueueCsr |= (1 << 6); 
 
    if (pPlxIntr->PciDmaChannel0) 
    { 
        // Check if DMA interrupt is routed to PCI 
        RegisterValue = 
            PLX_REG_READ( 
                pdx, 
                PCI9054_DMA0_MODE 
                ); 
 
        if (RegisterValue & (1 << 17)) 
        { 
            RegData.BitsToClear |= (1 << 18); 
 
            // Make sure DMA interrupt is routed to IOP 
            PLX_REG_WRITE( 
                pdx, 
                PCI9054_DMA0_MODE, 
                RegisterValue & ~(1 << 17) 
                ); 
        } 
    } 
 
    if (pPlxIntr->PciDmaChannel1) 
    { 
        // Check if DMA interrupt is routed to PCI 
        RegisterValue = 
            PLX_REG_READ( 
                pdx, 
                PCI9054_DMA1_MODE 
                ); 
 
        if (RegisterValue & (1 << 17)) 
        { 
            RegData.BitsToClear |= (1 << 19); 
 
            // Make sure DMA interrupt is routed to IOP 
            PLX_REG_WRITE( 
                pdx, 
                PCI9054_DMA1_MODE, 
                RegisterValue & ~(1 << 17) 
                ); 
        } 
    } 
 
    if (pPlxIntr->IopDmaChannel0) 
        RegData.BitsToClear |= (1 << 18); 
 
    if (pPlxIntr->IopDmaChannel1) 
        RegData.BitsToClear |= (1 << 19); 
 
    if (pPlxIntr->Mailbox0 || pPlxIntr->Mailbox1 || 
        pPlxIntr->Mailbox2 || pPlxIntr->Mailbox3) 
    { 
        RegData.BitsToClear |= (1 << 3); 
    } 
 
    if (pPlxIntr->PciDoorbell) 
        RegData.BitsToClear |= (1 << 9); 
 
    if (pPlxIntr->IopDoorbell) 
        RegData.BitsToClear |= (1 << 17); 
 
    if (pPlxIntr->PciMainInt) 
        RegData.BitsToClear |= (1 << 8); 
 
    if (pPlxIntr->IopToPciInt) 
        RegData.BitsToClear |= (1 << 11); 
 
    if (pPlxIntr->IopMainInt) 
        RegData.BitsToClear |= (1 << 16); 
 
    if (pPlxIntr->PciAbort) 
        RegData.BitsToClear |= (1 << 10); 
 
    if (pPlxIntr->AbortLSERR) 
        RegData.BitsToClear |= (1 << 0); 
 
    if (pPlxIntr->ParityLSERR) 
        RegData.BitsToClear |= (1 << 1); 
 
    if (pPlxIntr->RetryAbort) 
        RegData.BitsToClear |= (1 << 12); 
 
    if (pPlxIntr->PowerManagement) 
        RegData.BitsToClear |= (1 << 4); 
 
    if (pPlxIntr->LocalParityLSERR) 
        RegData.BitsToClear |= (1 << 6); 
 
 
    // Write register values if they have changed 
 
    if (RegData.BitsToClear != 0) 
    { 
        // Synchronize write of Interrupt Control/Status Register 
        KeSynchronizeExecution( 
            pdx->pInterruptObject, 
            PlxSynchronizedRegisterModify, 
            &RegData 
            ); 
    } 
 
    if (QueueCsr != QueueCsr_Original) 
    { 
        PLX_REG_WRITE( 
            pdx, 
            PCI9054_FIFO_CTRL_STAT, 
            QueueCsr 
            ); 
    } 
 
    // Power Management Interrupt 
    if (pPlxIntr->PciPME) 
    { 
        // Verify that the capability is enabled 
        if (PlxIsNewCapabilityEnabled( 
                pdx, 
                CAPABILITY_POWER_MANAGEMENT 
                ) == TRUE) 
        { 
            PLX_PCI_REG_READ( 
                pdx, 
                PCI9054_PM_CSR, 
                &RegisterValue 
                ); 
 
            // Disable PME interrupt 
            RegisterValue &= ~(1 << 8); 
 
            PLX_PCI_REG_WRITE( 
                pdx, 
                PCI9054_PM_CSR, 
                RegisterValue 
                ); 
        } 
    } 
 
    // Hot Swap Interrupt 
    if (pPlxIntr->Enum) 
    { 
        // Verify that the capability is enabled 
        if (PlxIsNewCapabilityEnabled( 
                pdx, 
                CAPABILITY_HOT_SWAP 
                ) == TRUE) 
        { 
            PLX_PCI_REG_READ( 
                pdx, 
                PCI9054_HS_CAP_ID, 
                &RegisterValue 
                ); 
 
            // Turn off INS & EXT bits so we don't clear them 
            RegisterValue &= ~((1 << 23) | (1 << 22)); 
 
            // Enable Enum Interrupt mask 
            RegisterValue |= (1 << 17); 
 
            PLX_PCI_REG_WRITE( 
                pdx, 
                PCI9054_HS_CAP_ID, 
                RegisterValue 
                ); 
        } 
    } 
 
    return ApiSuccess; 
} 
 
 
 
 
/****************************************************************************** 
 * 
 * Function   :  PlxPciIntrStatusGet 
 * 
 * Description:  Returns the last interrupts to occur on this device 
 * 
 ******************************************************************************/ 
RETURN_CODE 
PlxPciIntrStatusGet( 
    DEVICE_EXTENSION *pdx, 
    PLX_INTR         *pPlxIntr 
    ) 
{ 
    RtlZeroMemory( 
        pPlxIntr, 
        sizeof(PLX_INTR) 
        ); 
 
    if (pdx->InterruptSource & INTR_TYPE_LOCAL_1) 
    { 
        pPlxIntr->IopToPciInt = 1; 
    } 
 
    if (pdx->InterruptSource & INTR_TYPE_DOORBELL) 
    { 
        pPlxIntr->PciDoorbell = 1; 
    } 
 
    if (pdx->InterruptSource & INTR_TYPE_PCI_ABORT) 
    { 
        pPlxIntr->PciAbort = 1; 
    } 
 
    if (pdx->InterruptSource & INTR_TYPE_DMA_0) 
    { 
        pPlxIntr->PciDmaChannel0 = 1; 
    } 
 
    if (pdx->InterruptSource & INTR_TYPE_DMA_1) 
    { 
        pPlxIntr->PciDmaChannel1 = 1; 
    } 
 
    if (pdx->InterruptSource & INTR_TYPE_OUTBOUND_POST) 
    { 
        pPlxIntr->OutboundPost = 1; 
    } 
 
    return ApiSuccess; 
} 
 
 
 
 
/****************************************************************************** 
 * 
 * Function   :  PlxPciPowerLevelSet 
 * 
 * Description:  Set the Power level 
 * 
 ******************************************************************************/ 
RETURN_CODE 
PlxPciPowerLevelSet( 
    DEVICE_EXTENSION *pdx, 
    PLX_POWER_LEVEL   PowerLevel 
    ) 
{ 
    U32 RegisterValue; 
 
 
    // Check if we support the power state 
    if (PlxIsPowerLevelSupported( 
            pdx, 
            PowerLevel 
            ) == FALSE) 
    { 
        return ApiInvalidPowerState; 
    } 
 
    // Get the power state 
    PLX_PCI_REG_READ( 
        pdx, 
        PCI9054_PM_CSR, 
        &RegisterValue 
        ); 
 
    // Update the power state 
    RegisterValue = (RegisterValue & ~0x3) | (PowerLevel - D0); 
 
    // Set the new power state 
    PLX_PCI_REG_WRITE( 
        pdx, 
        PCI9054_PM_CSR, 
        RegisterValue 
        ); 
 
    return ApiSuccess; 
} 
 
 
 
 
/****************************************************************************** 
 * 
 * Function   :  PlxPciPowerLevelGet 
 * 
 * Description:  Get the Power Level 
 * 
 ******************************************************************************/ 
RETURN_CODE 
PlxPciPowerLevelGet( 
    DEVICE_EXTENSION *pdx, 
    PLX_POWER_LEVEL  *pPowerLevel 
    ) 
{ 
    U32 RegisterValue; 
 
 
    // Check if New capabilities are enabled 
    if (PlxIsNewCapabilityEnabled( 
            pdx, 
            CAPABILITY_POWER_MANAGEMENT 
            ) == FALSE) 
    { 
        *pPowerLevel = D0; 
        return ApiInvalidPowerState; 
    } 
 
    // Get power state 
    PLX_PCI_REG_READ( 
        pdx, 
        PCI9054_PM_CSR, 
        &RegisterValue 
        ); 
 
    *pPowerLevel = (PLX_POWER_LEVEL)((RegisterValue & 0x3) + D0); 
 
    return ApiSuccess; 
} 
 
 
 
 
/****************************************************************************** 
 * 
 * Function   :  PlxPciPmNcpRead 
 * 
 * Description:  Read the Power Management Next Capabilities Pointer 
 * 
 ******************************************************************************/ 
RETURN_CODE 
PlxPciPmNcpRead( 
    DEVICE_EXTENSION *pdx, 
    U8               *pValue 
    ) 
{ 
    U32 RegisterValue; 
 
 
    // Check if New capabilities are enabled 
    if (PlxIsNewCapabilityEnabled( 
            pdx, 
            CAPABILITY_POWER_MANAGEMENT 
            ) == FALSE) 
    { 
        *pValue = (U8)-1; 
        return ApiPMNotSupported; 
    } 
 
    PLX_PCI_REG_READ( 
        pdx, 
        PCI9054_PM_CAP_ID, 
        &RegisterValue 
        ); 
 
    *pValue = (U8)((RegisterValue >> 8) & 0xFF); 
 
    return ApiSuccess; 
} 
 
 
 
 
/****************************************************************************** 
 * 
 * Function   :  PlxPciHotSwapNcpRead 
 * 
 * Description:  Read the HotSwap Next Capabilities Pointer 
 * 
 ******************************************************************************/ 
RETURN_CODE 
PlxPciHotSwapNcpRead( 
    DEVICE_EXTENSION *pdx, 
    U8               *pValue 
    ) 
{ 
    U32 RegisterValue; 
 
 
    // Check if New capabilities are enabled 
    if (PlxIsNewCapabilityEnabled( 
            pdx, 
            CAPABILITY_HOT_SWAP 
            ) == FALSE) 
    { 
        *pValue = (U8)-1; 
        return ApiHSNotSupported; 
    } 
 
    PLX_PCI_REG_READ( 
        pdx, 
        PCI9054_HS_CAP_ID, 
        &RegisterValue 
        ); 
 
    *pValue = (U8)((RegisterValue >> 8) & 0xFF); 
 
    return ApiSuccess; 
} 
 
 
 
 
/****************************************************************************** 
 * 
 * Function   :  PlxPciHotSwapStatus 
 * 
 * Description:  Read the HotSwap status register 
 * 
 ******************************************************************************/ 
RETURN_CODE 
PlxPciHotSwapStatus( 
    DEVICE_EXTENSION *pdx, 
    U8               *pValue 
    ) 
{ 
    U32 RegisterValue; 
 
 
    // Check if New capabilities are enabled 
    if (PlxIsNewCapabilityEnabled( 
            pdx, 
            CAPABILITY_HOT_SWAP 
            ) == FALSE) 
    { 
        *pValue = (U8)-1; 
        return ApiHSNotSupported; 
    } 
 
    PLX_PCI_REG_READ( 
        pdx, 
        PCI9054_HS_CAP_ID, 
        &RegisterValue 
        ); 
 
    // Clear bits other than status 
    RegisterValue  = RegisterValue >> 16; 
    RegisterValue &= (HS_LED_ON | HS_BOARD_REMOVED | HS_BOARD_INSERTED); 
 
    *pValue = (U8)RegisterValue; 
 
    return ApiSuccess; 
} 
 
 
 
 
/****************************************************************************** 
 * 
 * Function   :  PlxPciVpdNcpRead 
 * 
 * Description:  Read the Vital Product Data Next Capabilities Pointer 
 * 
 ******************************************************************************/ 
RETURN_CODE 
PlxPciVpdNcpRead( 
    DEVICE_EXTENSION *pdx, 
    U8               *pValue 
    ) 
{ 
    U32 RegisterValue; 
 
 
    // Check if New capabilities are enabled 
    if (PlxIsNewCapabilityEnabled( 
            pdx, 
            CAPABILITY_VPD 
            ) == FALSE) 
    { 
        *pValue = (U8)-1; 
        return ApiVpdNotEnabled; 
    } 
 
    PLX_PCI_REG_READ( 
        pdx, 
        PCI9054_VPD_CAP_ID, 
        &RegisterValue 
        ); 
 
    *pValue = (U8)((RegisterValue >> 8) & 0xFF); 
 
    return ApiSuccess; 
} 
 
 
 
 
/****************************************************************************** 
 * 
 * Function   :  PlxPciVpdRead 
 * 
 * Description:  Read the Vital Product Data 
 * 
 ******************************************************************************/ 
RETURN_CODE 
PlxPciVpdRead( 
    DEVICE_EXTENSION *pdx, 
    U16               offset, 
    U32              *pValue 
    ) 
{ 
    S16 VpdRetries; 
    S16 VpdPollCount; 
    U32 RegisterValue; 
 
 
    // Check for unaligned offset 
    if (offset & 0x3) 
    { 
        *pValue = (U32)-1; 
        return ApiInvalidOffset; 
    } 
 
    // Prepare VPD command 
    RegisterValue = ((U32)offset << 16) | 0x3; 
 
    VpdRetries = VPD_COMMAND_MAX_RETRIES; 
    do 
    { 
        /************************************** 
        *  This loop will continue until the 
        *  VPD reports success or until we reach 
        *  the maximum number of retries 
        **************************************/ 
 
        // Send VPD Command 
        PLX_PCI_REG_WRITE( 
            pdx, 
            PCI9054_VPD_CAP_ID, 
            RegisterValue 
            ); 
 
        // Poll until VPD operation has completed 
        VpdPollCount = VPD_STATUS_MAX_POLL; 
        do 
        { 
            // Delay for a bit for VPD operation 
            Plx_sleep(VPD_STATUS_POLL_DELAY); 
 
            // Get VPD Status 
            PLX_PCI_REG_READ( 
                pdx, 
                PCI9054_VPD_CAP_ID, 
                &RegisterValue 
                ); 
 
            // Check for command completion 
            if (RegisterValue & (1 << 31)) 
            { 
                /******************************************* 
                * The VPD successfully read the EEPROM. Get 
                * the value & return a status of SUCCESS. 
                *******************************************/ 
 
                // Get the VPD Data result 
                PLX_PCI_REG_READ( 
                    pdx, 
                    PCI9054_VPD_DATA, 
                    &RegisterValue 
                    ); 
 
                *pValue = RegisterValue; 
 
                return ApiSuccess; 
            } 
        } 
        while (VpdPollCount--); 
    } 
    while (VpdRetries--); 
 
    /****************************************** 
    * VPD access failed if we reach this  
    * point - return an ERROR status 
    *******************************************/ 
 
    DebugPrintf(("ERROR - PlxPciVpdRead() failed, VPD timeout\n")); 
 
    *pValue = (U32)-1; 
 
    return ApiFailed; 
} 
 
 
 
 
/****************************************************************************** 
 * 
 * Function   :  PlxPciVpdWrite 
 * 
 * Description:  Write to the Vital Product Data 
 * 
 ******************************************************************************/ 
RETURN_CODE 
PlxPciVpdWrite( 
    DEVICE_EXTENSION *pdx, 
    U16               offset, 
    U32               VpdData 
    ) 
{ 
    S16 VpdRetries; 
    S16 VpdPollCount; 
    U32 RegisterValue; 
 
 
    // Check for unaligned offset 
    if (offset & 0x3) 
        return ApiInvalidOffset; 
 
    // Put write value into VPD Data register 
    PLX_PCI_REG_WRITE( 
        pdx, 
        PCI9054_VPD_DATA, 
        VpdData 
        ); 
 
    // Prepare VPD command 
    RegisterValue = (1 << 31) | ((U32)offset << 16) | 0x3; 
 
    VpdRetries = VPD_COMMAND_MAX_RETRIES; 
    do 
    { 
        /************************************** 
        *  This loop will continue until the 
        *  VPD reports success or until we reach 
        *  the maximum number of retries 
        **************************************/ 
 
        // Send VPD command 
        PLX_PCI_REG_WRITE( 
            pdx, 
            PCI9054_VPD_CAP_ID, 
            RegisterValue 
            ); 
 
        // Poll until VPD operation has completed 
        VpdPollCount = VPD_STATUS_MAX_POLL; 
        do 
        { 
            // Delay for a bit for VPD operation 
            Plx_sleep(VPD_STATUS_POLL_DELAY); 
 
            // Get VPD Status 
            PLX_PCI_REG_READ( 
                pdx, 
                PCI9054_VPD_CAP_ID, 
                &RegisterValue 
                ); 
 
            // Check for command completion 
            if ((RegisterValue & (1 << 31)) == 0) 
            { 
                /******************************************* 
                * The VPD successfully wrote to the EEPROM. 
                *******************************************/ 
                return ApiSuccess; 
            } 
        } 
        while (VpdPollCount--); 
    } 
    while (VpdRetries--); 
 
    /****************************************** 
    * VPD access failed if we reach this  
    * point - return an ERROR status 
    *******************************************/ 
 
    DebugPrintf(("ERROR - PlxPciVpdWrite() failed, VPD timeout\n")); 
 
    return ApiFailed; 
} 
 
 
 
 
/****************************************************************************** 
 * 
 * Function   :  PlxEepromPresent 
 * 
 * Description:  Determine if a programmed EEPROM is present on the device 
 * 
 ******************************************************************************/ 
RETURN_CODE 
PlxEepromPresent( 
    DEVICE_EXTENSION *pdx, 
    BOOLEAN          *pFlag 
    ) 
{ 
    U32 RegisterValue; 
 
 
    // Get EEPROM status register 
    RegisterValue = 
        PLX_REG_READ( 
            pdx, 
            PCI9054_EEPROM_CTRL_STAT 
            ); 
 
    if (RegisterValue & (1 << 28)) 
    { 
        *pFlag = TRUE; 
    } 
    else 
    { 
        *pFlag = FALSE; 
    } 
 
    return ApiSuccess; 
} 
 
 
 
 
/****************************************************************************** 
 * 
 * Function   :  PlxEepromReadByOffset 
 * 
 * Description:  Read a value from the EEPROM at a specified offset 
 * 
 ******************************************************************************/ 
RETURN_CODE 
PlxEepromReadByOffset( 
    DEVICE_EXTENSION *pdx, 
    U16               offset, 
    U32              *pValue 
    ) 
{ 
    U32         RegValue; 
    BOOLEAN     bUseVpd; 
    RETURN_CODE rc; 
 
 
    // Verify the offset 
    if ((offset & 0x3) || (offset > 0x200)) 
    { 
        DebugPrintf(("ERROR - Invalid EEPROM offset\n")); 
        return ApiInvalidOffset; 
    } 
 
    /**************************************************************** 
     * Note:  In the 9054, the EEPROM can be accessed either 
     *        through the VPD or by the EEPROM control register. 
     * 
     *        However, the EEPROM control register does not work 
     *        in the 9054 AA version and VPD access often fails. 
     *        The 9054 AB version fixes the EEPROM control register 
     *        access, but VPD may still fail. 
     * 
     *        As a result, PLX software does the following: 
     * 
     *          if (AB or newer chip) 
     *              Use EEPROM Control Register 
     *          else 
     *              Use VPD access 
     * 
     *        Additionally, there is no way to determine the 9054 
     *        version from the Host/PCI side.  To solve this, the 
     *        PCI Revision ID is used to "tell" the host which 
     *        version the 9054 is.  This value is either set by the 
     *        EEPROM or the local CPU, which has the ability to 
     *        determine the 9054 version.  The protocol is: 
     * 
     *          if (Hard-coded RevisionID != 0xA)  // Chip is not AA or AB 
     *              Use EEPROM Control Register 
     *          else 
     *          { 
     *              if (PCIRevisionID == 0xb) 
     *                  Use EEPROM Control Register 
     *              else 
     *                  Use VPD access 
     *          } 
     ***************************************************************/ 
 
    // Default to VPD access 
    bUseVpd = TRUE; 
 
    // Get hard-coded revision ID 
    RegValue = 
        PLX_REG_READ( 
            pdx, 
            PCI9054_REVISION_ID 
            ); 
 
    // Check if chip is other than AA or AB 
    if (RegValue != 0xA) 
    { 
        bUseVpd = FALSE; 
    } 
    else 
    { 
        // Get PCI Revision ID 
        PLX_PCI_REG_READ( 
            pdx, 
            PCI9054_REV_ID, 
            &RegValue 
            ); 
 
        // Check reported 9054 Version 
        if ((RegValue & 0xFF) == 0xB) 
        { 
            // 9054 AB version is reported 
            bUseVpd = FALSE; 
        } 
    } 
 
    // Access the EEPROM 
    if (bUseVpd == FALSE) 
    { 
        // 9054 AB or newer revision, so use EEPROM register 
 
        // Read EEPROM 
        Pci9000_EepromReadByOffset( 
            pdx, 
            Eeprom93CS56, 
            offset, 
            pValue 
            ); 
    } 
    else 
    { 
        // 9054 AA or unspecified version, so use VPD access 
 
        // Check if New capabilities are enabled 
        if (PlxIsNewCapabilityEnabled( 
                pdx, 
                CAPABILITY_VPD 
                ) == FALSE) 
        { 
            return ApiVpdNotEnabled; 
        } 
 
        // Read EEPROM value 
        rc = 
            PlxPciVpdRead( 
                pdx, 
                offset, 
                pValue 
                ); 
 
        if (rc != ApiSuccess) 
            return ApiFailed; 
    } 
 
    DebugPrintf(( 
        "EEPROM Offset %02X = %08X\n", 
        offset, 
        *pValue 
        )); 
 
    return ApiSuccess; 
} 
 
 
 
 
/****************************************************************************** 
 * 
 * Function   :  PlxEepromWriteByOffset 
 * 
 * Description:  Write a 32-bit value to the EEPROM at a specified offset 
 * 
 ******************************************************************************/ 
RETURN_CODE 
PlxEepromWriteByOffset( 
    DEVICE_EXTENSION *pdx, 
    U16               offset, 
    U32               value 
    ) 
{ 
    U32         RegValue; 
    U32         RegisterSave; 
    BOOLEAN     bUseVpd; 
    RETURN_CODE rc; 
 
 
    // Verify the offset 
    if ((offset & 0x3) || (offset > 0x200)) 
    { 
        DebugPrintf(("ERROR - Invalid EEPROM offset\n")); 
        return ApiInvalidOffset; 
    } 
 
    // Unprotect the EEPROM for write access 
    RegisterSave = 
        PLX_REG_READ( 
            pdx, 
            PCI9054_ENDIAN_DESC 
            ); 
 
    PLX_REG_WRITE( 
        pdx, 
        PCI9054_ENDIAN_DESC, 
        RegisterSave & ~(0xFF << 16) 
        ); 
 
    /**************************************************************** 
     * Note:  In the 9054, the EEPROM can be accessed either 
     *        through the VPD or by the EEPROM control register. 
     * 
     *        However, the EEPROM control register does not work 
     *        in the 9054 AA version and VPD access often fails. 
     *        The 9054 AB version fixes the EEPROM control register 
     *        access, but VPD may still fail. 
     * 
     *        As a result, PLX software does the following: 
     * 
     *          if (AB or newer chip) 
     *              Use EEPROM Control Register 
     *          else 
     *              Use VPD access 
     * 
     *        Additionally, there is no way to determine the 9054 
     *        version from the Host/PCI side.  To solve this, the 
     *        PCI Revision ID is used to "tell" the host which 
     *        version the 9054 is.  This value is either set by the 
     *        EEPROM or the local CPU, which has the ability to 
     *        determine the 9054 version.  The protocol is: 
     * 
     *          if (Hard-coded RevisionID != 0xA)  // Chip is not AA or AB 
     *              Use EEPROM Control Register 
     *          else 
     *          { 
     *              if (PCIRevisionID == 0xb) 
     *                  Use EEPROM Control Register 
     *              else 
     *                  Use VPD access 
     *          } 
     ***************************************************************/ 
 
    // Default to VPD access 
    bUseVpd = TRUE; 
 
    // Get hard-coded revision ID 
    RegValue = 
        PLX_REG_READ( 
            pdx, 
            PCI9054_REVISION_ID 
            ); 
 
    // Check if chip is other than AA or AB 
    if (RegValue != 0xA) 
    { 
        bUseVpd = FALSE; 
    } 
    else 
    { 
        // Get PCI Revision ID 
        PLX_PCI_REG_READ( 
            pdx, 
            PCI9054_REV_ID, 
            &RegValue 
            ); 
 
        // Check reported 9054 Version 
        if ((RegValue & 0xFF) == 0xB) 
        { 
            // 9054 AB version is reported 
            bUseVpd = FALSE; 
        } 
    } 
 
    // Access the EEPROM 
    if (bUseVpd == FALSE) 
    { 
        // 9054 AB or newer revision, so use EEPROM register 
 
        // Write to EEPROM 
        Pci9000_EepromWriteByOffset( 
            pdx, 
            Eeprom93CS56, 
            offset, 
            value 
            ); 
    } 
    else 
    { 
        // 9054 AA or unspecified version, so use VPD access 
 
        // Check if New capabilities are enabled 
        if (PlxIsNewCapabilityEnabled( 
                pdx, 
                CAPABILITY_VPD 
                ) == FALSE) 
        { 
            return ApiVpdNotEnabled; 
        } 
 
        // Write value to the EEPROM 
        rc = 
            PlxPciVpdWrite( 
                pdx, 
                offset, 
                value 
                ); 
 
        if (rc != ApiSuccess) 
        { 
            // Restore EEPROM Write-Protected Address Boundary 
            PLX_REG_WRITE( 
                pdx, 
                PCI9054_ENDIAN_DESC, 
                RegisterSave 
                ); 
 
            return ApiFailed; 
        } 
    } 
 
    // Restore EEPROM Write-Protected Address Boundary 
    PLX_REG_WRITE( 
        pdx, 
        PCI9054_ENDIAN_DESC, 
        RegisterSave 
        ); 
 
    DebugPrintf(( 
        "Wrote %08X to EEPROM Offset %02X\n", 
        value, 
        offset 
        )); 
 
    return ApiSuccess; 
} 
 
 
 
 
/****************************************************************************** 
 * 
 * Function   :  PlxRegisterMailboxRead 
 * 
 * Description:  Reads a valid Mailbox register from the PLX device 
 * 
 ******************************************************************************/ 
RETURN_CODE 
PlxRegisterMailboxRead( 
    DEVICE_EXTENSION *pdx, 
    MAILBOX_ID        MailboxId, 
    U32              *pValue 
    ) 
{ 
    // Verify Mailbox ID 
    if (MailboxId < MailBox0 || MailboxId > MailBox7) 
    { 
        *pValue = (U32)-1; 
        return ApiInvalidRegister; 
    } 
 
    // Check if Mailbox 0 or 1 
    if (MailboxId == MailBox0 || MailboxId == MailBox1) 
    { 
        *pValue = 
            PLX_REG_READ( 
                pdx, 
                PCI9054_MAILBOX0 + (MailboxId * sizeof(U32)) 
                ); 
    } 
    else 
    { 
        // Mailboxes 2 to 7 are not based from Malibox 0 
        *pValue = 
            PLX_REG_READ( 
                pdx, 
                PCI9054_MAILBOX2 + ((MailboxId-2) * sizeof(U32)) 
                ); 
    } 
 
    return ApiSuccess; 
} 
 
 
 
 
/****************************************************************************** 
 * 
 * Function   :  PlxRegisterMailboxWrite 
 * 
 * Description:  Write to one of the PLX device's Mailbox registers 
 * 
 ******************************************************************************/ 
RETURN_CODE 
PlxRegisterMailboxWrite( 
    DEVICE_EXTENSION *pdx, 
    MAILBOX_ID        MailboxId, 
    U32               value 
    ) 
{ 
    // Verify Mailbox ID 
    if (MailboxId < MailBox0 || MailboxId > MailBox7) 
    { 
        return ApiInvalidRegister; 
    } 
 
    if (MailboxId == MailBox0 || MailboxId == MailBox1) 
    { 
        PLX_REG_WRITE( 
            pdx, 
            PCI9054_MAILBOX0 + (MailboxId * sizeof(U32)), 
            value 
            ); 
    } 
    else 
    { 
        PLX_REG_WRITE( 
            pdx, 
            PCI9054_MAILBOX2 + ((MailboxId-2) * sizeof(U32)), 
            value 
            ); 
    } 
 
    return ApiSuccess; 
} 
 
 
 
 
/****************************************************************************** 
 * 
 * Function   :  PlxRegisterDoorbellRead 
 * 
 * Description:  Returns the last doorbell interrupt value 
 * 
 ******************************************************************************/ 
RETURN_CODE 
PlxRegisterDoorbellRead( 
    DEVICE_EXTENSION *pdx, 
    U32              *pValue 
    ) 
{ 
    *pValue = pdx->IntrDoorbellValue; 
 
    return ApiSuccess; 
} 
 
 
 
 
/****************************************************************************** 
 * 
 * Function   :  PlxRegisterDoorbellWrite 
 * 
 * Description:  Sets the local Doorbell Register 
 * 
 ******************************************************************************/ 
RETURN_CODE 
PlxRegisterDoorbellWrite( 
    DEVICE_EXTENSION *pdx, 
    U32               value 
    ) 
{ 
    PLX_REG_WRITE( 
        pdx, 
        PCI9054_LOCAL_DOORBELL, 
        value 
        ); 
 
    return ApiSuccess; 
} 
 
 
 
 
/****************************************************************************** 
 * 
 * Function   :  PlxMuInboundPortRead 
 * 
 * Description:  Read the Inbound messaging port of a PLX device 
 * 
 ******************************************************************************/ 
RETURN_CODE 
PlxMuInboundPortRead( 
    DEVICE_EXTENSION *pdx, 
    U32              *pFrame 
    ) 
{ 
    *pFrame = 
        PLX_REG_READ( 
            pdx, 
            0x40 
            ); 
 
    return ApiSuccess; 
} 
 
 
 
 
/****************************************************************************** 
 * 
 * Function   :  PlxMuInboundPortWrite 
 * 
 * Description:  Write a posted message frame to the inbound port 
 * 
 ******************************************************************************/ 
RETURN_CODE 
PlxMuInboundPortWrite( 
    DEVICE_EXTENSION *pdx, 
    U32               Frame 
    ) 
{ 
    PLX_REG_WRITE( 
        pdx, 
        0x40, 
        Frame 
        ); 
 
    return ApiSuccess; 
} 
 
 
 
 
/****************************************************************************** 
 * 
 * Function   :  PlxMuOutboundPortRead 
 * 
 * Description:  Reads the posted message frame from the outbound port 
 * 
 ******************************************************************************/ 
RETURN_CODE 
PlxMuOutboundPortRead( 
    DEVICE_EXTENSION *pdx, 
    U32              *pFrame 
    ) 
{ 
    *pFrame = 
        PLX_REG_READ( 
            pdx, 
            0x44 
            ); 
 
    return ApiSuccess; 
} 
 
 
 
 
/****************************************************************************** 
 * 
 * Function   :  PlxMuOutboundPortWrite 
 * 
 * Description:  Writes to the outbound port with a free message frame 
 * 
 ******************************************************************************/ 
RETURN_CODE 
PlxMuOutboundPortWrite( 
    DEVICE_EXTENSION *pdx, 
    U32               Frame 
    ) 
{ 
    PLX_REG_WRITE( 
        pdx, 
        0x44, 
        Frame 
        ); 
 
    return ApiSuccess; 
} 
 
 
 
 
/****************************************************************************** 
 * 
 * Function   :  PlxDmaControl 
 * 
 * Description:  Control the DMA engine 
 * 
 ******************************************************************************/ 
RETURN_CODE 
PlxDmaControl( 
    DEVICE_EXTENSION *pdx, 
    DMA_CHANNEL       channel, 
    DMA_COMMAND       command 
    ) 
{ 
    U8  i; 
    U8  shift; 
    U32 RegValue; 
 
 
    // Verify valid DMA channel 
    switch (channel) 
    { 
        case PrimaryPciChannel0: 
            i     = 0; 
            shift = 0; 
            break; 
 
        case PrimaryPciChannel1: 
            i     = 1; 
            shift = 8; 
            break; 
 
        default: 
            DebugPrintf(("ERROR - Invalid DMA channel\n")); 
            return ApiDmaChannelInvalid; 
    } 
 
    // Verify that this channel has been opened 
    if (pdx->DmaInfo[i].state == DmaStateClosed) 
    { 
        DebugPrintf(("ERROR - DMA Channel has not been opened\n")); 
        return ApiDmaChannelUnavailable; 
    } 
 
    switch (command) 
    { 
        case DmaPause: 
            // Pause the DMA Channel 
            RegValue = 
                PLX_REG_READ( 
                    pdx, 
                    PCI9054_DMA_COMMAND_STAT 
                    ); 
 
            PLX_REG_WRITE( 
                pdx, 
                PCI9054_DMA_COMMAND_STAT, 
                RegValue & ~((1 << 0) << shift) 
                ); 
 
            // Check if the transfer has completed 
            RegValue = 
                PLX_REG_READ( 
                    pdx, 
                    PCI9054_DMA_COMMAND_STAT 
                    ); 
 
            if (RegValue & ((1 << 4) << shift)) 
                return ApiDmaDone; 
            break; 
 
        case DmaResume: 
            // Verify that the DMA Channel is paused 
            RegValue = 
                PLX_REG_READ( 
                    pdx, 
                    PCI9054_DMA_COMMAND_STAT 
                    ); 
 
            if ((RegValue & (((1 << 4) | (1 << 0)) << shift)) == 0) 
            { 
                PLX_REG_WRITE( 
                    pdx, 
                    PCI9054_DMA_COMMAND_STAT, 
                    RegValue | ((1 << 0) << shift) 
                    ); 
            } 
            else 
            { 
                return ApiDmaNotPaused; 
            } 
            break; 
 
        case DmaAbort: 
            // Pause the DMA Channel 
            RegValue = 
                PLX_REG_READ( 
                    pdx, 
                    PCI9054_DMA_COMMAND_STAT 
                    ); 
 
            PLX_REG_WRITE( 
                pdx, 
                PCI9054_DMA_COMMAND_STAT, 
                RegValue & ~((1 << 0) << shift) 
                ); 
 
            // Check if the transfer has completed 
            RegValue = 
                PLX_REG_READ( 
                    pdx, 
                    PCI9054_DMA_COMMAND_STAT 
                    ); 
 
            if (RegValue & ((1 << 4) << shift)) 
                return ApiDmaDone; 
 
            // Abort the transfer (should cause an interrupt) 
            PLX_REG_WRITE( 
                pdx, 
                PCI9054_DMA_COMMAND_STAT, 
                RegValue | ((1 << 2) << shift) 
                ); 
            break; 
 
        default: 
            return ApiDmaCommandInvalid; 
    } 
 
    return ApiSuccess; 
} 
 
 
 
 
/****************************************************************************** 
 * 
 * Function   :  PlxDmaStatus 
 * 
 * Description:  Get status of a DMA channel 
 * 
 ******************************************************************************/ 
RETURN_CODE 
PlxDmaStatus( 
    DEVICE_EXTENSION *pdx, 
    DMA_CHANNEL       channel 
    ) 
{ 
    U32 RegValue; 
 
 
    // Verify valid DMA channel 
    switch (channel) 
    { 
        case PrimaryPciChannel0: 
        case PrimaryPciChannel1: 
            break; 
 
        default: 
            DebugPrintf(("ERROR - Invalid DMA channel\n")); 
            return ApiDmaChannelInvalid; 
    } 
 
    // Return the current DMA status 
 
    RegValue = 
        PLX_REG_READ( 
            pdx, 
            PCI9054_DMA_COMMAND_STAT 
            ); 
 
    if (channel == PrimaryPciChannel1) 
        RegValue = RegValue >> 8; 
 
    if ((RegValue & ((1 << 4) | (1 << 0))) == 0) 
        return ApiDmaPaused; 
 
    if (RegValue & (1 << 4)) 
        return ApiDmaDone; 
 
    return ApiDmaInProgress; 
} 
 
 
 
 
/****************************************************************************** 
 * 
 * Function   :  PlxDmaBlockChannelOpen 
 * 
 * Description:  Requests usage of a device's DMA channel 
 * 
 ******************************************************************************/ 
RETURN_CODE 
PlxDmaBlockChannelOpen( 
    DEVICE_EXTENSION *pdx, 
    DMA_CHANNEL       channel, 
    DMA_CHANNEL_DESC *pDesc, 
    VOID             *pOwner 
    ) 
{ 
    U8           i; 
    U32          mode; 
    U32          RegValue; 
    U32          threshold; 
    KIRQL        OriginalIrqL; 
    PLX_REG_DATA RegData; 
 
 
    // Verify valid DMA channel 
    switch (channel) 
    { 
        case PrimaryPciChannel0: 
            i = 0; 
            break; 
 
        case PrimaryPciChannel1: 
            i = 1; 
            break; 
 
        default: 
            DebugPrintf(("ERROR - Invalid DMA channel\n")); 
            return ApiDmaChannelInvalid; 
    } 
 
    KeAcquireSpinLock( 
        &(pdx->Lock_DmaChannel), 
        &OriginalIrqL 
        ); 
 
    // Verify that we can open the channel 
    if (pdx->DmaInfo[i].state != DmaStateClosed) 
    { 
        DebugPrintf(("ERROR - DMA channel already opened\n")); 
 
        KeReleaseSpinLock( 
            &(pdx->Lock_DmaChannel), 
            OriginalIrqL 
            ); 
 
        return ApiDmaChannelUnavailable; 
    } 
 
    // Open the channel 
    pdx->DmaInfo[i].state = DmaStateBlock; 
 
    // Record the Owner 
    pdx->DmaInfo[i].pOwner = pOwner; 
 
    // Mark DMA as free 
    pdx->DmaInfo[i].bPending = FALSE; 
 
    KeReleaseSpinLock( 
        &(pdx->Lock_DmaChannel), 
        OriginalIrqL 
        ); 
 
    // Setup for synchronized access to Interrupt register 
    RegData.pdx         = pdx; 
    RegData.offset      = PCI9054_INT_CTRL_STAT; 
    RegData.BitsToClear = 0; 
 
    KeAcquireSpinLock( 
        &(pdx->Lock_HwAccess), 
        &OriginalIrqL 
        ); 
 
    // Get DMA priority 
    RegValue = 
        PLX_REG_READ( 
            pdx, 
            PCI9054_LOCAL_DMA_ARBIT 
            ); 
 
    // Clear priority 
    RegValue &= ~((1 << 20) | (1 << 19)); 
 
    // Set the priority 
    switch (pDesc->DmaChannelPriority) 
    { 
        case Channel0Highest: 
            PLX_REG_WRITE( 
                pdx, 
                PCI9054_LOCAL_DMA_ARBIT, 
                RegValue | (1 << 19) 
                ); 
            break; 
 
        case Channel1Highest: 
            PLX_REG_WRITE( 
                pdx, 
                PCI9054_LOCAL_DMA_ARBIT, 
                RegValue | (1 << 20) 
                ); 
            break; 
 
        case Rotational: 
            PLX_REG_WRITE( 
                pdx, 
                PCI9054_LOCAL_DMA_ARBIT, 
                RegValue 
                ); 
            break; 
 
        default: 
            DebugPrintf(( 
                "WARNING - DmaChannelOpen() invalid priority state\n" 
                )); 
            break; 
    } 
 
 
    threshold = 
        (pDesc->TholdForIopWrites <<  0) | 
        (pDesc->TholdForIopReads  <<  4) | 
        (pDesc->TholdForPciWrites <<  8) | 
        (pDesc->TholdForPciReads  << 12); 
 
    mode = 
        (0 <<  9) |                  // No Chaining 
        (1 << 10) |                  // Enable DMA Done interrupt 
        (1 << 17) |                  // Route interrupts to PCI 
        (pDesc->IopBusWidth              <<  0) | 
        (pDesc->WaitStates               <<  2) | 
        (pDesc->EnableReadyInput         <<  6) | 
        (pDesc->EnableBTERMInput         <<  7) | 
        (pDesc->EnableIopBurst           <<  8) | 
        (pDesc->HoldIopAddrConst         << 11) | 
        (pDesc->DemandMode               << 12) | 
        (pDesc->EnableWriteInvalidMode   << 13) | 
        (pDesc->EnableDmaEOTPin          << 14) | 
        (pDesc->DmaStopTransferMode      << 15) | 
        (pDesc->EnableDualAddressCycles  << 18); 
 
    // Get DMA Threshold 
    RegValue = 
        PLX_REG_READ( 
            pdx, 
            PCI9054_DMA_THRESHOLD 
            ); 
 
    if (channel == PrimaryPciChannel0) 
    { 
        // Setup threshold 
        PLX_REG_WRITE( 
            pdx, 
            PCI9054_DMA_THRESHOLD, 
            (RegValue & 0xffff0000) | threshold 
            ); 
 
        // Write DMA mode 
        PLX_REG_WRITE( 
            pdx, 
            PCI9054_DMA0_MODE, 
            mode 
            ); 
 
        // Enable DMA Channel interrupt 
        RegData.BitsToSet = (1 << 18); 
        KeSynchronizeExecution( 
            pdx->pInterruptObject, 
            PlxSynchronizedRegisterModify, 
            &RegData 
            ); 
    } 
    else 
    { 
        // Setup threshold 
        PLX_REG_WRITE( 
            pdx, 
            PCI9054_DMA_THRESHOLD, 
            (RegValue & 0x0000ffff) | (threshold << 16) 
            ); 
 
        // Write DMA mode 
        PLX_REG_WRITE( 
            pdx, 
            PCI9054_DMA1_MODE, 
            mode 
            ); 
 
        // Enable DMA Channel interrupt 
        RegData.BitsToSet = (1 << 19); 
        KeSynchronizeExecution( 
            pdx->pInterruptObject, 
            PlxSynchronizedRegisterModify, 
            &RegData 
            ); 
    } 
 
    KeReleaseSpinLock( 
        &(pdx->Lock_HwAccess), 
        OriginalIrqL 
        ); 
 
    return ApiSuccess; 
} 
 
 
 
 
/****************************************************************************** 
 * 
 * Function   :  PlxDmaBlockTransfer 
 * 
 * Description:  Performs DMA block transfer 
 * 
 ******************************************************************************/ 
RETURN_CODE 
PlxDmaBlockTransfer( 
    DEVICE_EXTENSION     *pdx, 
    DMA_CHANNEL           channel, 
    DMA_TRANSFER_ELEMENT *pDmaData 
    ) 
{ 
    U8    i; 
    U8    shift; 
    U16   OffsetDmaBase; 
    U16   OffsetPciAddrHigh; 
    U32   RegValue; 
    KIRQL OriginalIrqL; 
 
 
    // Setup transfer registers 
    switch (channel) 
    { 
        case PrimaryPciChannel0: 
            i                 = 0; 
            shift             = 0; 
            OffsetDmaBase     = PCI9054_DMA0_MODE; 
            OffsetPciAddrHigh = PCI9054_DMA0_PCI_DAC; 
            break; 
 
        case PrimaryPciChannel1: 
            i                 = 1; 
            shift             = 8; 
            OffsetDmaBase     = PCI9054_DMA1_MODE; 
            OffsetPciAddrHigh = PCI9054_DMA1_PCI_DAC; 
            break; 
 
        default: 
            DebugPrintf(("ERROR - Invalid DMA channel\n")); 
            return ApiDmaChannelInvalid; 
    } 
 
    // Verify that DMA is not in progress 
    RegValue = 
        PLX_REG_READ( 
            pdx, 
            PCI9054_DMA_COMMAND_STAT 
            ); 
 
    if ((RegValue & ((1 << 4) << shift)) == 0) 
    { 
        DebugPrintf(("ERROR - DMA Channel is currently active\n")); 
        return ApiDmaInProgress; 
    } 
 
    KeAcquireSpinLock( 
        &(pdx->Lock_DmaChannel), 
        &OriginalIrqL 
        ); 
 
    // Verify DMA Channel was opened correctly 
    if (pdx->DmaInfo[i].state != DmaStateBlock) 
    { 
        DebugPrintf(("ERROR - DMA Channel has not been opened for Block DMA\n")); 
 
        KeReleaseSpinLock( 
            &(pdx->Lock_DmaChannel), 
            OriginalIrqL 
            ); 
 
        return ApiDmaChannelUnavailable; 
    } 
 
    KeReleaseSpinLock( 
        &(pdx->Lock_DmaChannel), 
        OriginalIrqL 
        ); 
 
    KeAcquireSpinLock( 
        &(pdx->Lock_HwAccess), 
        &OriginalIrqL 
        ); 
 
    // Make sure DMA done interrupt is enabled 
    RegValue = 
        PLX_REG_READ( 
            pdx, 
            OffsetDmaBase 
            ); 
 
    RegValue |= (1 << 10); 
 
    PLX_REG_WRITE( 
        pdx, 
        OffsetDmaBase, 
        RegValue 
        ); 
 
    // Write PCI Address 
    PLX_REG_WRITE( 
        pdx, 
        OffsetDmaBase + 0x4, 
        pDmaData->u.PciAddrLow 
        ); 
 
    // Write Local Address 
    PLX_REG_WRITE( 
        pdx, 
        OffsetDmaBase + 0x8, 
        pDmaData->LocalAddr 
        ); 
 
    // Write Transfer Count 
    PLX_REG_WRITE( 
        pdx, 
        OffsetDmaBase + 0xc, 
        pDmaData->TransferCount 
        ); 
 
    // Write Descriptor Pointer 
    RegValue = (pDmaData->TerminalCountIntr << 2) | 
               (pDmaData->LocalToPciDma     << 3); 
 
    PLX_REG_WRITE( 
        pdx, 
        OffsetDmaBase + 0x10, 
        RegValue 
        ); 
 
    // Write the high PCI address 
    PLX_REG_WRITE( 
        pdx, 
        OffsetPciAddrHigh, 
        pDmaData->PciAddrHigh 
        ); 
 
    // Enable DMA Channel 
    RegValue = 
        PLX_REG_READ( 
            pdx, 
            PCI9054_DMA_COMMAND_STAT 
            ); 
 
    PLX_REG_WRITE( 
        pdx, 
        PCI9054_DMA_COMMAND_STAT, 
        RegValue | ((1 << 0) << shift) 
        ); 
 
    KeReleaseSpinLock( 
        &(pdx->Lock_HwAccess), 
        OriginalIrqL 
        ); 
 
    DebugPrintf(("Starting DMA transfer...\n")); 
 
    // Start DMA 
    PLX_REG_WRITE( 
        pdx, 
        PCI9054_DMA_COMMAND_STAT, 
        RegValue | (((1 << 0) | (1 << 1)) << shift) 
        ); 
 
    return ApiSuccess; 
} 
 
 
 
 
/****************************************************************************** 
 * 
 * Function   :  PlxDmaBlockChannelClose 
 * 
 * Description:  Close a previously opened channel 
 * 
 ******************************************************************************/ 
RETURN_CODE 
PlxDmaBlockChannelClose( 
    DEVICE_EXTENSION *pdx, 
    DMA_CHANNEL       channel, 
    BOOLEAN           bCheckInProgress 
    ) 
{ 
    U8    i; 
    U32   RegValue; 
    KIRQL OriginalIrqL; 
 
 
    // Verify valid DMA channel 
    switch (channel) 
    { 
        case PrimaryPciChannel0: 
            i = 0; 
            break; 
 
        case PrimaryPciChannel1: 
            i = 1; 
            break; 
 
        default: 
            DebugPrintf(("ERROR - Invalid DMA channel\n")); 
            return ApiDmaChannelInvalid; 
    } 
 
    if (bCheckInProgress) 
    { 
        // Verify that DMA is not in progress 
        RegValue = 
            PLX_REG_READ( 
                pdx, 
                PCI9054_DMA_COMMAND_STAT 
                ); 
 
        if (channel == PrimaryPciChannel1) 
            RegValue = RegValue >> 8; 
 
        if ((RegValue & (1 << 4)) == 0) 
        { 
            if (RegValue & (1 << 0)) 
                return ApiDmaInProgress; 
            else 
                return ApiDmaPaused; 
        } 
    } 
 
    KeAcquireSpinLock( 
        &(pdx->Lock_DmaChannel), 
        &OriginalIrqL 
        ); 
 
    // Verify DMA Channel was opened correctly 
    if (pdx->DmaInfo[i].state != DmaStateBlock) 
    { 
        DebugPrintf(("ERROR - DMA Channel has not been opened for Block DMA\n")); 
 
        KeReleaseSpinLock( 
            &(pdx->Lock_DmaChannel), 
            OriginalIrqL 
            ); 
 
        return ApiDmaChannelUnavailable; 
    } 
 
    // Close the channel 
    pdx->DmaInfo[i].state = DmaStateClosed; 
 
    // Clear owner information 
    pdx->DmaInfo[i].pOwner = NULL; 
 
    KeReleaseSpinLock( 
        &(pdx->Lock_DmaChannel), 
        OriginalIrqL 
        ); 
 
    return ApiSuccess; 
} 
 
 
 
 
/****************************************************************************** 
 * 
 * Function   :  PlxDmaSglChannelOpen 
 * 
 * Description:  Open a DMA channel for SGL mode 
 * 
 ******************************************************************************/ 
RETURN_CODE 
PlxDmaSglChannelOpen( 
    DEVICE_EXTENSION *pdx, 
    DMA_CHANNEL       channel, 
    DMA_CHANNEL_DESC *pDesc, 
    VOID             *pOwner 
    ) 
{ 
    U8           i; 
    U32          mode; 
    U32          RegValue; 
    U32          threshold; 
    KIRQL        OriginalIrqL; 
    PLX_REG_DATA RegData; 
 
 
    // Verify valid DMA channel 
    switch (channel) 
    { 
        case PrimaryPciChannel0: 
            i = 0; 
            break; 
 
        case PrimaryPciChannel1: 
            i = 1; 
            break; 
 
        default: 
            DebugPrintf(("ERROR - Invalid DMA channel\n")); 
            return ApiDmaChannelInvalid; 
    } 
 
    KeAcquireSpinLock( 
        &(pdx->Lock_DmaChannel), 
        &OriginalIrqL 
        ); 
 
    // Verify that we can open the channel 
    if (pdx->DmaInfo[i].state != DmaStateClosed) 
    { 
        DebugPrintf(("ERROR - DMA channel already opened\n")); 
 
        KeReleaseSpinLock( 
            &(pdx->Lock_DmaChannel), 
            OriginalIrqL 
            ); 
 
        return ApiDmaChannelUnavailable; 
    } 
 
    // Open the channel 
    pdx->DmaInfo[i].state = DmaStateSgl; 
 
    // Record the Owner 
    pdx->DmaInfo[i].pOwner = pOwner; 
 
    // Mark DMA as free 
    pdx->DmaInfo[i].bPending = FALSE; 
 
    KeReleaseSpinLock( 
        &(pdx->Lock_DmaChannel), 
        OriginalIrqL 
        ); 
 
    // Setup for synchronized access to Interrupt register 
    RegData.pdx         = pdx; 
    RegData.offset      = PCI9054_INT_CTRL_STAT; 
    RegData.BitsToClear = 0; 
 
    KeAcquireSpinLock( 
        &(pdx->Lock_HwAccess), 
        &OriginalIrqL 
        ); 
 
    // Get DMA priority 
    RegValue = 
        PLX_REG_READ( 
            pdx, 
            PCI9054_LOCAL_DMA_ARBIT 
            ); 
 
    // Clear priority 
    RegValue &= ~((1 << 20) | (1 << 19)); 
 
    // Set the priority 
    switch (pDesc->DmaChannelPriority) 
    { 
        case Channel0Highest: 
            PLX_REG_WRITE( 
                pdx, 
                PCI9054_LOCAL_DMA_ARBIT, 
                RegValue | (1 << 19) 
                ); 
            break; 
 
        case Channel1Highest: 
            PLX_REG_WRITE( 
                pdx, 
                PCI9054_LOCAL_DMA_ARBIT, 
                RegValue | (1 << 20) 
                ); 
            break; 
 
        case Rotational: 
            PLX_REG_WRITE( 
                pdx, 
                PCI9054_LOCAL_DMA_ARBIT, 
                RegValue 
                ); 
            break; 
 
        default: 
            DebugPrintf(( 
                "WARNING - DmaChannelOpen() invalid priority state\n" 
                )); 
            break; 
    } 
 
 
    threshold = 
        (pDesc->TholdForIopWrites <<  0) | 
        (pDesc->TholdForIopReads  <<  4) | 
        (pDesc->TholdForPciWrites <<  8) | 
        (pDesc->TholdForPciReads  << 12); 
 
    mode = 
        (1 <<  9) |                  // Enable Chaining 
        (1 << 10) |                  // Enable DMA Done interrupt 
        (1 << 17) |                  // Route interrupts to PCI 
        (0 << 18) |                  // Disable Dual-Addressing 
        (pDesc->IopBusWidth              <<  0) | 
        (pDesc->WaitStates               <<  2) | 
        (pDesc->EnableReadyInput         <<  6) | 
        (pDesc->EnableBTERMInput         <<  7) | 
        (pDesc->EnableIopBurst           <<  8) | 
        (pDesc->HoldIopAddrConst         << 11) | 
        (pDesc->DemandMode               << 12) | 
        (pDesc->EnableWriteInvalidMode   << 13) | 
        (pDesc->EnableDmaEOTPin          << 14) | 
        (pDesc->DmaStopTransferMode      << 15); 
 
    // Keep track if local address should remain constant 
    if (pDesc->HoldIopAddrConst) 
        pdx->DmaInfo[i].bLocalAddrConstant = TRUE; 
    else 
        pdx->DmaInfo[i].bLocalAddrConstant = FALSE; 
 
    // Get DMA Threshold 
    RegValue = 
        PLX_REG_READ( 
            pdx, 
            PCI9054_DMA_THRESHOLD 
            ); 
 
    if (channel == PrimaryPciChannel0) 
    { 
        // Setup threshold 
        PLX_REG_WRITE( 
            pdx, 
            PCI9054_DMA_THRESHOLD, 
            (RegValue & 0xffff0000) | threshold 
            ); 
 
        // Write DMA mode 
        PLX_REG_WRITE( 
            pdx, 
            PCI9054_DMA0_MODE, 
            mode 
            ); 
 
        // Enable DMA Channel interrupt 
        RegData.BitsToSet = (1 << 18); 
        KeSynchronizeExecution( 
            pdx->pInterruptObject, 
            PlxSynchronizedRegisterModify, 
            &RegData 
            ); 
 
        // Clear Dual Address cycle register 
        PLX_REG_WRITE( 
            pdx, 
            PCI9054_DMA0_PCI_DAC, 
            0 
            ); 
    } 
    else 
    { 
        // Setup threshold 
        PLX_REG_WRITE( 
            pdx, 
            PCI9054_DMA_THRESHOLD, 
            (RegValue & 0x0000ffff) | (threshold << 16) 
            ); 
 
        // Write DMA mode 
        PLX_REG_WRITE( 
            pdx, 
            PCI9054_DMA1_MODE, 
            mode 
            ); 
 
        // Enable DMA Channel interrupt 
        RegData.BitsToSet = (1 << 19); 
        KeSynchronizeExecution( 
            pdx->pInterruptObject, 
            PlxSynchronizedRegisterModify, 
            &RegData 
            ); 
 
        // Clear Dual Address cycle register 
        PLX_REG_WRITE( 
            pdx, 
            PCI9054_DMA1_PCI_DAC, 
            0 
            ); 
    } 
 
    KeReleaseSpinLock( 
        &(pdx->Lock_HwAccess), 
        OriginalIrqL 
        ); 
 
    return ApiSuccess; 
} 
 
 
 
 
/****************************************************************************** 
 * 
 * Function   :  PlxDmaSglTransfer 
 * 
 * Description:  Performs a DMA SGL transfer 
 * 
 ******************************************************************************/ 
RETURN_CODE 
PlxDmaSglTransfer( 
    DEVICE_EXTENSION     *pdx, 
    DMA_CHANNEL           channel, 
    DMA_TRANSFER_ELEMENT *pDmaData 
    ) 
{ 
    U8    i; 
    U8    shift; 
    U16   OffsetDesc; 
    U32   RegValue; 
    U32   SglPciAddress; 
    KIRQL OriginalIrqL; 
 
 
    // Verify valid DMA channel 
    switch (channel) 
    { 
        case PrimaryPciChannel0: 
            i          = 0; 
            shift      = 0; 
            OffsetDesc = PCI9054_DMA0_DESC_PTR; 
            break; 
 
        case PrimaryPciChannel1: 
            i          = 1; 
            shift      = 8; 
            OffsetDesc = PCI9054_DMA1_DESC_PTR; 
            break; 
 
        default: 
            DebugPrintf(("ERROR - Invalid DMA channel\n")); 
            return ApiDmaChannelInvalid; 
    } 
 
    // Verify that DMA is not in progress 
    RegValue = 
        PLX_REG_READ( 
            pdx, 
            PCI9054_DMA_COMMAND_STAT 
            ); 
 
    if ((RegValue & ((1 << 4) << shift)) == 0) 
    { 
        DebugPrintf(("ERROR - DMA Channel is currently active\n")); 
        return ApiDmaInProgress; 
    } 
 
    KeAcquireSpinLock( 
        &(pdx->Lock_DmaChannel), 
        &OriginalIrqL 
        ); 
 
    // Verify DMA Channel was opened correctly 
    if (pdx->DmaInfo[i].state != DmaStateSgl) 
    { 
        DebugPrintf(("ERROR - DMA channel has not been opened for SGL DMA\n")); 
 
        KeReleaseSpinLock( 
            &(pdx->Lock_DmaChannel), 
            OriginalIrqL 
            ); 
 
        return ApiDmaChannelUnavailable; 
    } 
 
    // Verify a DMA transfer is not pending 
    if (pdx->DmaInfo[i].bPending) 
    { 
        DebugPrintf(( 
            "ERROR - A DMA transfer is currently in progress\n" 
            )); 
 
        KeReleaseSpinLock( 
            &(pdx->Lock_DmaChannel), 
            OriginalIrqL 
            ); 
 
        return ApiDmaInProgress; 
    } 
 
    // Set the DMA pending flag 
    pdx->DmaInfo[i].bPending = TRUE; 
 
    KeReleaseSpinLock( 
        &(pdx->Lock_DmaChannel), 
        OriginalIrqL 
        ); 
 
    SglPciAddress = 
        PlxLockBufferAndBuildSgl( 
            pdx, 
            i, 
            pDmaData 
            ); 
 
    if (SglPciAddress == 0) 
    { 
        DebugPrintf(("ERROR - Unable to lock buffer and build SGL list\n")); 
        pdx->DmaInfo[i].bPending = FALSE; 
        return ApiDmaSglBuildFailed; 
    } 
 
    KeAcquireSpinLock( 
        &(pdx->Lock_HwAccess), 
        &OriginalIrqL 
        ); 
 
    // Write SGL physical address & set descriptors in PCI space 
    PLX_REG_WRITE( 
        pdx, 
        OffsetDesc, 
        SglPciAddress | (1 << 0) 
        ); 
 
    RegValue = 
        PLX_REG_READ( 
            pdx, 
            PCI9054_DMA_COMMAND_STAT 
            ); 
 
    // Enable DMA channel 
    PLX_REG_WRITE( 
        pdx, 
        PCI9054_DMA_COMMAND_STAT, 
        RegValue | ((1 << 0) << shift) 
        ); 
 
    KeReleaseSpinLock( 
        &(pdx->Lock_HwAccess), 
        OriginalIrqL 
        ); 
 
    DebugPrintf(("Starting DMA transfer...\n")); 
 
    // Start DMA 
    PLX_REG_WRITE( 
        pdx, 
        PCI9054_DMA_COMMAND_STAT, 
        RegValue | (((1 << 0) | (1 << 1)) << shift) 
        ); 
 
    return ApiSuccess; 
} 
 
 
 
 
/****************************************************************************** 
 * 
 * Function   :  PlxDmaSglChannelClose 
 * 
 * Description:  Close a previously opened channel 
 * 
 ******************************************************************************/ 
RETURN_CODE 
PlxDmaSglChannelClose( 
    DEVICE_EXTENSION *pdx, 
    DMA_CHANNEL       channel, 
    BOOLEAN           bCheckInProgress 
    ) 
{ 
    U8    i; 
    U32   RegValue; 
    KIRQL OriginalIrqL; 
 
 
    // Verify valid DMA channel 
    switch (channel) 
    { 
        case PrimaryPciChannel0: 
            i = 0; 
            break; 
 
        case PrimaryPciChannel1: 
            i = 1; 
            break; 
 
        default: 
            DebugPrintf(("ERROR - Invalid DMA channel\n")); 
            return ApiDmaChannelInvalid; 
    } 
 
    if (bCheckInProgress) 
    { 
        // Verify that DMA is not in progress 
        RegValue = 
            PLX_REG_READ( 
                pdx, 
                PCI9054_DMA_COMMAND_STAT 
                ); 
 
        if (channel == PrimaryPciChannel1) 
            RegValue = RegValue >> 8; 
 
        if ((RegValue & (1 << 4)) == 0) 
        { 
            if (RegValue & (1 << 0)) 
                return ApiDmaInProgress; 
            else 
                return ApiDmaPaused; 
        } 
    } 
 
    KeAcquireSpinLock( 
        &(pdx->Lock_DmaChannel), 
        &OriginalIrqL 
        ); 
 
    // Verify DMA Channel was opened correctly 
    if (pdx->DmaInfo[i].state != DmaStateSgl) 
    { 
        DebugPrintf(("ERROR - DMA channel has not been opened for SGL DMA\n")); 
 
        KeReleaseSpinLock( 
            &(pdx->Lock_DmaChannel), 
            OriginalIrqL 
            ); 
 
        return ApiDmaChannelUnavailable; 
    } 
 
    // Close the channel 
    pdx->DmaInfo[i].state = DmaStateClosed; 
 
    // Clear owner information 
    pdx->DmaInfo[i].pOwner = NULL; 
 
    KeReleaseSpinLock( 
        &(pdx->Lock_DmaChannel), 
        OriginalIrqL 
        ); 
 
    // If DMA is hung, a transfer could be pending, so release user buffer 
    if (pdx->DmaInfo[i].bPending) 
    { 
        PlxSglDmaTransferComplete( 
            pdx, 
            i 
            ); 
    } 
 
    // Release memory previously used for SGL descriptors 
    if (pdx->DmaInfo[i].SglBuffer.pKernelVa != NULL) 
    { 
        DebugPrintf(( 
            "Releasing memory used for SGL descriptors...\n" 
            )); 
 
        Plx_dma_buffer_free( 
            pdx, 
            &pdx->DmaInfo[i].SglBuffer 
            ); 
    } 
 
    return ApiSuccess; 
}