www.pudn.com > 9054Src.rar > PciSupport.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: 
 * 
 *      PciSupport.c 
 * 
 * Description: 
 * 
 *      This file contains the PCI support functions 
 * 
 * Revision History: 
 * 
 *      02-01-06 : PCI SDK v4.40 
 * 
 ******************************************************************************/ 
 
 
#include "PciSupport.h" 
#include "PlugPlay.h" 
#include "PlxChip.h" 
 
 
 
 
/****************************************************************************** 
 * 
 * Function   :  PlxPciRegisterRead 
 * 
 * Description:  Reads a PCI register at the specified offset 
 * 
 ******************************************************************************/ 
U32 
PlxPciRegisterRead( 
    DEVICE_EXTENSION *pdx, 
    U8                bus, 
    U8                slot, 
    U16               offset, 
    RETURN_CODE      *pReturnCode 
    ) 
{ 
    U32            RegisterValue; 
    NTSTATUS       status; 
    PDEVICE_OBJECT fdo; 
 
 
    if (pdx->pDeviceObject == NULL) 
    { 
        if (pReturnCode != NULL) 
            *pReturnCode = ApiNullParam; 
        return (U32)-1; 
    } 
 
    if (pdx->pDeviceObject->DriverObject == NULL) 
    { 
        if (pReturnCode != NULL) 
            *pReturnCode = ApiNullParam; 
        return (U32)-1; 
    } 
 
    fdo = pdx->pDeviceObject->DriverObject->DeviceObject; 
 
    // Now parse through all the functional devices objects 
    while (fdo != NULL) 
    { 
        pdx = fdo->DeviceExtension; 
 
        if (pdx == NULL) 
        { 
            DebugPrintf(("ERROR - ConfigRegisterRead() NULL driver object\n")); 
            if (pReturnCode != NULL) 
                *pReturnCode = ApiConfigAccessFailed; 
            return (U32)-1; 
        } 
 
        // Compare the bus and slot numbers 
        if ((bus  == pdx->Device.BusNumber) && 
            (slot == pdx->Device.SlotNumber)) 
        { 
            // Read the register 
            status = 
                PciRegisterBufferRead( 
                    fdo, 
                    offset, 
                    &RegisterValue, 
                    sizeof(U32) 
                    ); 
 
            if ( !NT_SUCCESS(status) ) 
            { 
                DebugPrintf(("ERROR - Unable to read PCI Config Register\n")); 
                if (pReturnCode != NULL) 
                    *pReturnCode = ApiConfigAccessFailed; 
 
                return (U32)-1; 
            } 
 
            if (pReturnCode != NULL) 
                *pReturnCode = ApiSuccess; 
 
            return RegisterValue; 
        } 
 
        // Increment to next device 
        fdo = fdo->NextDevice; 
    } 
 
    // If we reach here, then the device was not located 
 
    if (pReturnCode != NULL) 
        *pReturnCode = ApiConfigAccessFailed; 
 
    return (U32)-1; 
} 
 
 
 
 
/****************************************************************************** 
 * 
 * Function   :  PlxPciRegisterWrite 
 * 
 * Description:  Writes a value to a PCI register at the specified offset 
 * 
 ******************************************************************************/ 
RETURN_CODE 
PlxPciRegisterWrite( 
    DEVICE_EXTENSION *pdx, 
    U8                bus, 
    U8                slot, 
    U16               offset, 
    U32               value 
    ) 
{ 
    NTSTATUS       status; 
    PDEVICE_OBJECT fdo; 
 
 
    if (pdx->pDeviceObject == NULL) 
        return ApiNullParam; 
 
    if (pdx->pDeviceObject->DriverObject == NULL) 
        return ApiNullParam; 
 
    fdo = pdx->pDeviceObject->DriverObject->DeviceObject; 
 
    // Now parse through all the functional devices objects 
    while (fdo != NULL) 
    { 
        pdx = fdo->DeviceExtension; 
 
        if (pdx == NULL) 
        { 
            DebugPrintf(("ERROR - ConfigRegisterRead() NULL driver object\n")); 
            return ApiConfigAccessFailed; 
        } 
 
        // Compare the bus and slot numbers 
        if ((bus  == pdx->Device.BusNumber) && 
            (slot == pdx->Device.SlotNumber)) 
        { 
            // Read the register 
            status = 
                PciRegisterBufferWrite( 
                    fdo, 
                    offset, 
                    value 
                    ); 
 
            if ( !NT_SUCCESS(status) ) 
            { 
                DebugPrintf(("ERROR - Unable to write PCI Config Register\n")); 
                return ApiConfigAccessFailed; 
            } 
 
            return ApiSuccess; 
        } 
 
        // Increment to next device 
        fdo = fdo->NextDevice; 
    } 
 
    // If we reach here, then the device was not located 
 
    return ApiConfigAccessFailed; 
} 
 
 
 
 
/****************************************************************************** 
 * 
 * Function   :  PciRegisterBufferRead 
 * 
 * Description:  Read a PCI register of a device using the PnP Bus Manager 
 * 
 ******************************************************************************/ 
NTSTATUS 
PciRegisterBufferRead( 
    PDEVICE_OBJECT  fdo, 
    U16             offset, 
    VOID           *buffer, 
    U16             size 
    ) 
{ 
    PIRP               pIrp; 
    KEVENT             event; 
    NTSTATUS           status; 
    PIO_STACK_LOCATION stack; 
 
 
    pIrp = 
        IoAllocateIrp( 
            fdo->StackSize, 
            FALSE 
            ); 
 
    if (pIrp == NULL) 
    { 
        DebugPrintf(("ERROR - ConfigRegisterBufferRead() unable to allocate IRP\n")); 
        return STATUS_INSUFFICIENT_RESOURCES; 
    } 
 
    // Initialize kernel event 
    KeInitializeEvent( 
        &event, 
        NotificationEvent, 
        FALSE 
        ); 
 
    stack = 
        IoGetNextIrpStackLocation( 
            pIrp 
            ); 
 
    // Fill the IRP 
    pIrp->IoStatus.Status                        = STATUS_NOT_SUPPORTED; 
    stack->MajorFunction                         = IRP_MJ_PNP; 
    stack->MinorFunction                         = IRP_MN_READ_CONFIG; 
    stack->Parameters.ReadWriteConfig.WhichSpace = PCI_WHICHSPACE_CONFIG; 
    stack->Parameters.ReadWriteConfig.Buffer     = buffer; 
    stack->Parameters.ReadWriteConfig.Offset     = offset; 
    stack->Parameters.ReadWriteConfig.Length     = size; 
 
    IoSetCompletionRoutine( 
        pIrp, 
        (PIO_COMPLETION_ROUTINE)OnRequestComplete, 
        (PVOID)&event, 
        TRUE, 
        TRUE, 
        TRUE 
        ); 
 
    // Send the packet 
    status = 
        IoCallDriver( 
            ((DEVICE_EXTENSION *) fdo->DeviceExtension)->pLowerDeviceObject, 
            pIrp 
            ); 
 
    if (status == STATUS_PENDING) 
    { 
        // Wait for completion 
        KeWaitForSingleObject( 
            &event, 
            Executive, 
            KernelMode, 
            FALSE, 
            NULL 
            ); 
 
        status = pIrp->IoStatus.Status; 
    } 
 
    // Release the IRP 
    IoFreeIrp( 
        pIrp 
        ); 
 
    return status; 
} 
 
 
 
 
/****************************************************************************** 
 * 
 * Function   :  PciRegisterBufferWrite 
 * 
 * Description:  Writes to a PCI register of a device using the PnP Bus Manager 
 * 
 ******************************************************************************/ 
NTSTATUS 
PciRegisterBufferWrite( 
    PDEVICE_OBJECT  fdo, 
    U16             offset, 
    U32             value 
    ) 
{ 
    PIRP               pIrp; 
    KEVENT             event; 
    NTSTATUS           status; 
    PIO_STACK_LOCATION stack; 
 
 
    pIrp = 
        IoAllocateIrp( 
            fdo->StackSize, 
            FALSE 
            ); 
 
    if (pIrp == NULL) 
    { 
        DebugPrintf(("ERROR - ConfigRegisterBufferWrite() unable to allocate IRP\n")); 
        return STATUS_INSUFFICIENT_RESOURCES; 
    } 
 
    // Initialize kernel event 
    KeInitializeEvent( 
        &event, 
        NotificationEvent, 
        FALSE 
        ); 
 
    stack = 
        IoGetNextIrpStackLocation( 
            pIrp 
            ); 
 
    // Fill the IRP 
    pIrp->IoStatus.Status                        = STATUS_NOT_SUPPORTED; 
    stack->MajorFunction                         = IRP_MJ_PNP; 
    stack->MinorFunction                         = IRP_MN_WRITE_CONFIG; 
    stack->Parameters.ReadWriteConfig.WhichSpace = PCI_WHICHSPACE_CONFIG; 
    stack->Parameters.ReadWriteConfig.Buffer     = &value; 
    stack->Parameters.ReadWriteConfig.Offset     = offset; 
    stack->Parameters.ReadWriteConfig.Length     = sizeof(U32); 
 
    IoSetCompletionRoutine( 
        pIrp, 
        (PIO_COMPLETION_ROUTINE)OnRequestComplete, 
        (PVOID)&event, 
        TRUE, 
        TRUE, 
        TRUE 
        ); 
 
    // Send the packet 
    status = 
        IoCallDriver( 
            ((DEVICE_EXTENSION *)fdo->DeviceExtension)->pLowerDeviceObject, 
            pIrp 
            ); 
 
    if (status == STATUS_PENDING) 
    { 
        // Wait for completion 
        KeWaitForSingleObject( 
            &event, 
            Executive, 
            KernelMode, 
            FALSE, 
            NULL 
            ); 
        status = pIrp->IoStatus.Status; 
    } 
 
    IoFreeIrp( 
        pIrp 
        ); 
 
    return STATUS_SUCCESS; 
} 
 
 
 
 
/****************************************************************************** 
 * 
 * Function   :  PlxPciRegisterRead_Unsupported 
 * 
 * Description:  Reads a PCI register by bypassing the OS services 
 * 
 ******************************************************************************/ 
U32 
PlxPciRegisterRead_Unsupported( 
    DEVICE_EXTENSION *pdx, 
    U8                bus, 
    U8                slot, 
    U16               offset, 
    RETURN_CODE      *pReturnCode 
    ) 
{ 
    U32   function; 
    U32   RegSave; 
    U32   RegValue; 
    KIRQL IrqlSave; 
 
 
    /*************************************************************** 
     * Access of a PCI register involves using I/O addresses 0xcf8 
     * and 0xcfc. These addresses must be used together and no other 
     * process must interrupt. 
     * 
     * Note:  On Multi-Processor systems, the I/O ports may still be 
     *        accessed by another CPU.  Raising the IRQL does not 
     *        prevent other CPUs from accessing the ports since 
     *        they are not a resource owned by this driver. 
     **************************************************************/ 
 
    // The function number is specified in the upper 3-bits of the slot 
    function = (slot >> 5) & 0x7; 
 
    // Make sure upper 3-bits are cleared 
    slot &= 0x1F; 
 
    // Raise the IRQL to prevent context switches during access 
    KeRaiseIrql( 
        DISPATCH_LEVEL, 
        &IrqlSave 
        ); 
 
    // Save the content of the command register 
    RegSave = 
        IO_PORT_READ_32( 
            0xcf8 
            ); 
 
    // Configure the command register to access the desired location 
    IO_PORT_WRITE_32( 
        0xcf8, 
        (1 << 31) | (bus << 16) | (slot << 11) | (function << 8) | offset 
        ); 
 
    // Read the register 
    RegValue = 
        IO_PORT_READ_32( 
            0xcfc 
            ); 
 
    // Restore the command register 
    IO_PORT_WRITE_32( 
        0xcf8, 
        RegSave 
        ); 
 
    // Restore IRQL 
    KeLowerIrql( 
        IrqlSave 
        ); 
 
    if (pReturnCode != NULL) 
        *pReturnCode = ApiSuccess; 
 
    return RegValue; 
} 
 
 
 
 
/****************************************************************************** 
 * 
 * Function   :  PlxPciRegisterWrite_Unsupported 
 * 
 * Description:  Writes to a PCI register by bypassing the OS services 
 * 
 ******************************************************************************/ 
RETURN_CODE 
PlxPciRegisterWrite_Unsupported( 
    DEVICE_EXTENSION *pdx, 
    U8                bus, 
    U8                slot, 
    U16               offset, 
    U32               value 
    ) 
{ 
    U8    function; 
    U32   RegSave; 
    KIRQL IrqlSave; 
 
 
    /*************************************************************** 
     * Access of a PCI register involves using I/O addresses 0xcf8 
     * and 0xcfc. These addresses must be used together and no other 
     * process must interrupt. 
     * 
     * Note:  On Multi-Processor systems, the I/O ports may still be 
     *        accessed by another CPU.  Raising the IRQL does not 
     *        prevent other CPUs from accessing the ports since 
     *        they are not a resource owned by this driver. 
     **************************************************************/ 
 
    // The function number is specified in the upper 3-bits of the slot 
    function = (slot >> 5) & 0x7; 
 
    // Make sure upper 3-bits are cleared 
    slot &= 0x1F; 
 
    // Raise the IRQL to prevent context switches during access 
    KeRaiseIrql( 
        DISPATCH_LEVEL, 
        &IrqlSave 
        ); 
 
    // Save the content of the command register 
    RegSave = 
        IO_PORT_READ_32( 
            0xcf8 
            ); 
 
    // Configure the command register to access the desired location 
    IO_PORT_WRITE_32( 
        0xcf8, 
        (1 << 31) | (bus << 16) | (slot << 11) | (function << 8) | offset 
        ); 
 
    // Write the register 
    IO_PORT_WRITE_32( 
        0xcfc, 
        value 
        ); 
 
    // Restore the command register 
    IO_PORT_WRITE_32( 
        0xcf8, 
        RegSave 
        ); 
 
    // Restore IRQL 
    KeLowerIrql( 
        IrqlSave 
        ); 
 
    return ApiSuccess; 
} 
 
 
 
 
/****************************************************************************** 
 * 
 * Function   :  GetBusSlotNumber 
 * 
 * Description:  Get the bus and slot number of a supported device. 
 *               This function does not work for the slot number. 
 * 
 ******************************************************************************/ 
NTSTATUS 
GetBusSlotNumber( 
    PDEVICE_OBJECT    pdo, 
    DEVICE_EXTENSION *pdx, 
    U32               CompareValue 
    ) 
{ 
    U8       slot; 
    U32      ResultLength; 
    U32      PropertyBuffer; 
    U32      RegValue; 
    U32      RegisterSave; 
    NTSTATUS status; 
 
 
    ResultLength = 0; 
 
    // Get the bus number 
    status = 
        IoGetDeviceProperty( 
             pdo, 
             DevicePropertyBusNumber, 
             sizeof(U32), 
             &PropertyBuffer, 
             &ResultLength 
             ); 
 
    if ( !NT_SUCCESS(status) ) 
    { 
        DebugPrintf(( 
            "ERROR - IoGetDeviceProperty() unable to get bus number, code = %Xh\n", 
            status 
            )); 
 
        return status; 
    } 
 
    // Verify that the function did write back a U32 
    if (ResultLength != sizeof(U32)) 
    { 
        DebugPrintf(( 
            "ERROR - IoGetDeviceProperty() invalid ResultLength (%d)\n", 
            ResultLength 
            )); 
 
        return STATUS_UNSUCCESSFUL; 
    } 
 
    // Store the Bus number 
    pdx->Device.BusNumber = (U8)PropertyBuffer; 
 
 
    /************************************************************************** 
     * Now attempt to get the slot number 
     * 
     * Note: The IoGetDeviceProperty() function is not implemented correctly in 
     *       Windows 98.  The code has been left here in case this is fixed in a 
     *       future release.  If the IoGetDeviceProperty() function is unsuccessful, 
     *       a workaround is provided to retrieve the correct slot number. 
     **************************************************************************/ 
 
    ResultLength = 0; 
 
    status = 
        IoGetDeviceProperty( 
            pdo, 
            DevicePropertyAddress, 
            sizeof(U32), 
            &PropertyBuffer, 
            &ResultLength 
            ); 
 
    if ( NT_SUCCESS(status) ) 
    { 
        if (ResultLength != sizeof(U32)) 
        { 
            DebugPrintf(( 
                "ERROR - IoGetDeviceProperty() invalid ResultLength (%d)\n", 
                ResultLength 
                )); 
 
            return STATUS_UNSUCCESSFUL; 
        } 
 
        pdx->Device.SlotNumber = (U8)(PropertyBuffer >> 16); 
 
        DebugPrintf(( 
            "Device information - PCI bus %d, slot %d, function %d\n", 
            pdx->Device.BusNumber, 
            (U8)(PropertyBuffer >> 16), 
            (U8)PropertyBuffer 
            )); 
 
        return STATUS_SUCCESS; 
    } 
 
 
    /**************************************************************** 
    * We were unable to get the slot number, so another method is 
    * required.  Our workaround is to scan the PCI bus and find the 
    * device whose BAR0 matches the current device. 
    ****************************************************************/ 
 
    DebugPrintf(("WARNING - IoGetDeviceProperty() unable to get slot number, code = ")); 
 
    switch (status) 
    { 
        case STATUS_BUFFER_TOO_SMALL: 
            DebugPrintf_NoInfo(("STATUS_BUFFER_TOO_SMALL (req=%d bytes)\n", ResultLength)); 
            break; 
 
        case STATUS_NOT_IMPLEMENTED: 
            DebugPrintf_NoInfo(("STATUS_NOT_IMPLEMENTED\n")); 
            break; 
 
        case STATUS_NOT_FOUND: 
            DebugPrintf_NoInfo(("STATUS_NOT_FOUND\n")); 
            break; 
 
        default: 
            DebugPrintf_NoInfo(("%08xh\n", status)); 
            break; 
    } 
 
    DebugPrintf(("NOTE: Implementing workaround to get Slot number\n")); 
 
    // Scan the PCI bus to find our device 
    for (slot = 0; slot < MAX_PCI_DEV; slot++) 
    { 
        // Read the BAR register 
        RegValue = 
            PlxPciRegisterRead_Unsupported( 
                pdx, 
                pdx->Device.BusNumber, 
                slot, 
                CFG_BAR0, 
                NULL 
                ); 
 
        // Compare with our device 
        if (RegValue == CompareValue) 
        { 
            pdx->Device.SlotNumber = slot; 
 
            DebugPrintf(( 
                "Workaround successful, device information - PCI bus %d, slot %d, function 0\n", 
                pdx->Device.BusNumber, 
                slot 
                )); 
 
            return STATUS_SUCCESS; 
        } 
    } 
 
    DebugPrintf(("ERROR - Unable to implement workaround to get Slot number\n")); 
 
    pdx->Device.SlotNumber = -1; 
 
    return STATUS_UNSUCCESSFUL; 
} 
 
 
 
 
/****************************************************************************** 
 * 
 * Function   :  PlxLockDevice 
 * 
 * Description:  Lock a device for operation, return FALSE if device is being 
 *               removed. 
 * 
 ******************************************************************************/ 
BOOLEAN 
PlxLockDevice( 
    DEVICE_EXTENSION *pdx 
    ) 
{ 
    S32 usage; 
 
 
    // Increment use count on our device object 
    usage = 
        InterlockedIncrement( 
            &pdx->usage 
            ); 
 
    DebugPrintf_NoInfo(("\n")); 
 
    /*  
       If device is about to be removed, restore the use count and return FALSE. 
       If a race exists with HandleRemoveDevice (maybe running on another CPU), 
       the sequence we've followed is guaranteed to avoid a mistaken deletion of 
       the device object. If we test "removing" after HandleRemoveDevice sets  
       it, we'll restore the use count and return FALSE. In the meantime, if 
       HandleRemoveDevice decremented count to 0 before we did our increment, 
       its thread will have set the remove event. Otherwise, we'll decrement to 
       0 and set the event. Either way, HandleRemoveDevice will wake up to  
       finish removing the device, and we'll return FALSE to our caller. 
 
       If, on the other hand, we test "removing" before HandleRemoveDevice sets 
       it, we'll have already incremented the use count past 1 and will return  
       TRUE. Our caller will eventually call UnlockDevice, which will decrement 
       the use count and might set the event HandleRemoveDevice is waiting on at 
       that point. 
    */ 
    if (pdx->removing) 
    { 
        // Removing device 
        if (InterlockedDecrement(&pdx->usage) == 0) 
        { 
            KeSetEvent( 
                &pdx->evRemove, 
                0, 
                FALSE 
                ); 
        } 
 
        return FALSE; 
    } 
 
    return TRUE; 
} 
 
 
 
 
/****************************************************************************** 
 * 
 * Function   :  PlxUnlockDevice 
 * 
 * Description:  Unlock a device 
 * 
 ******************************************************************************/ 
VOID 
PlxUnlockDevice( 
    DEVICE_EXTENSION *pdx 
    ) 
{ 
    S32 usage; 
 
 
    usage = 
        InterlockedDecrement( 
            &pdx->usage 
            ); 
 
    if (usage == 0) 
    { 
        // Removing device 
        KeSetEvent( 
            &pdx->evRemove, 
            0, 
            FALSE 
            ); 
    } 
}