Exemple #1
0
/*******************************************************************************
 *
 * Function   :  Plx_cleanup_module
 *
 * Description:  Unload the driver
 *
 ******************************************************************************/
void
Plx_cleanup_module(
    void
)
{
    DebugPrintf_Cont(("\n"));
    DebugPrintf(("Unload driver...\n"));

    // Release common buffer
    if (pGbl_DriverObject->CommonBuffer.Size != 0)
    {
        DebugPrintf(("De-allocate Common Buffer\n"));

        // Release the buffer
        Plx_dma_buffer_free(
            pGbl_DriverObject->DeviceObject->DeviceExtension,   // First device
            &(pGbl_DriverObject->CommonBuffer)
        );
    }

    // De-register driver
    if (pGbl_DriverObject->bPciDriverReg)
        pci_unregister_driver( &PlxPciDriver );

    DebugPrintf((
                    "De-register driver (MajorID = %03d)\n",
                    pGbl_DriverObject->MajorID
                ));

    /*********************************************************
     * De-register the driver with the OS
     *
     * NOTE: This driver still uses the old method for de-registering
     * the device (unregister_chrdev) for compatability with 2.4 kernels.
     * A future version of the driver may use the new interface
     * (cdev_init, cdev_add, & cdev_del).
     ********************************************************/
    unregister_chrdev(
        pGbl_DriverObject->MajorID,
        PLX_DRIVER_NAME
    );

    DebugPrintf((
                    "Release global driver object (%p)\n",
                    pGbl_DriverObject
                ));

    // Release driver object
    kfree( pGbl_DriverObject );
    pGbl_DriverObject = NULL;

    DebugPrintf(("...driver unloaded\n"));
}
Exemple #2
0
/******************************************************************************
 *
 * Function   :  PlxChip_DmaChannelClose
 *
 * Description:  Close a previously opened channel
 *
 ******************************************************************************/
PLX_STATUS
PlxChip_DmaChannelClose(
    DEVICE_EXTENSION *pdx,
    U8                channel,
    BOOLEAN           bCheckInProgress,
    VOID             *pOwner
    )
{
    PLX_STATUS status;


    DebugPrintf(("Closing DMA channel %d...\n", channel));

    // Verify valid DMA channel
    switch (channel)
    {
        case 0:
        case 1:
            break;

        default:
            DebugPrintf(("ERROR - Invalid DMA channel\n"));
            return ApiDmaChannelInvalid;
    }

    // Verify DMA channel was opened
    if (pdx->DmaInfo[channel].bOpen == FALSE)
    {
        DebugPrintf(("ERROR - DMA channel has not been opened\n"));
        return ApiDmaChannelUnavailable;
    }

    // Verify owner
    if (pdx->DmaInfo[channel].pOwner != pOwner)
    {
        DebugPrintf(("ERROR - DMA owned by different process\n"));
        return ApiDeviceInUse;
    }

    // Check DMA status
    status =
        PlxChip_DmaStatus(
            pdx,
            channel,
            pOwner
            );

    // Verify DMA is not in progress
    if (status != ApiDmaDone)
    {
        // DMA is still in progress
        if (bCheckInProgress)
            return status;

        DebugPrintf(("DMA in progress, aborting...\n"));

        // Force DMA abort, which may generate a DMA done interrupt
        PlxChip_DmaControl(
            pdx,
            channel,
            DmaAbort,
            pOwner
            );

        // Small delay to let driver cleanup if DMA interrupts
        Plx_sleep( 100 );
    }

    spin_lock(
        &(pdx->Lock_Dma[channel])
        );

    // Close the channel
    pdx->DmaInfo[channel].bOpen = FALSE;

    // Clear owner information
    pdx->DmaInfo[channel].pOwner = NULL;

    spin_unlock(
        &(pdx->Lock_Dma[channel])
        );

    // If DMA is hung, an SGL transfer could be pending, so release user buffer
    if (pdx->DmaInfo[channel].bSglPending)
    {
        PlxSglDmaTransferComplete(
            pdx,
            channel
            );
    }

    // Release memory previously used for SGL descriptors
    if (pdx->DmaInfo[channel].SglBuffer.pKernelVa != NULL)
    {
        DebugPrintf(("Releasing memory used for SGL descriptors...\n"));

        Plx_dma_buffer_free(
            pdx,
            &pdx->DmaInfo[channel].SglBuffer
            );
    }

    return ApiSuccess;
}
Exemple #3
0
/*******************************************************************************
 *
 * Function   :  PlxPciPhysicalMemoryFree
 *
 * Description:  Free previously allocated physically contiguous page-locked memory
 *
 ******************************************************************************/
PLX_STATUS
PlxPciPhysicalMemoryFree(
    DEVICE_EXTENSION *pdx,
    PLX_PHYSICAL_MEM *pPciMem
    )
{
    struct list_head    *pEntry;
    PLX_PHYS_MEM_OBJECT *pMemObject;


    spin_lock(
        &(pdx->Lock_PhysicalMemList)
        );

    pEntry = pdx->List_PhysicalMem.next;

    // Traverse list to find the desired list object
    while (pEntry != &(pdx->List_PhysicalMem))
    {
        // Get the object
        pMemObject =
            list_entry(
                pEntry,
                PLX_PHYS_MEM_OBJECT,
                ListEntry
                );

        // Check if the physical addresses matches
        if (pMemObject->BusPhysical == pPciMem->PhysicalAddr)
        {
            // Remove the object from the list
            list_del(
                pEntry
                );

            spin_unlock(
                &(pdx->Lock_PhysicalMemList)
                );

            // Release the buffer
            Plx_dma_buffer_free(
                pdx,
                pMemObject
                );

            // Release the list object
            kfree(
                pMemObject
                );

            return ApiSuccess;
        }

        // Jump to next item in the list
        pEntry = pEntry->next;
    }

    spin_unlock(
        &(pdx->Lock_PhysicalMemList)
        );

    DebugPrintf(("ERROR - buffer object not found in list\n"));

    return ApiInvalidData;
}
Exemple #4
0
/*******************************************************************************
 *
 * Function   :  PlxLockBufferAndBuildSgl
 *
 * Description:  Lock a user buffer and build an SGL for it
 *
 ******************************************************************************/
PLX_STATUS
PlxLockBufferAndBuildSgl(
    DEVICE_EXTENSION *pdx,
    U8                channel,
    PLX_DMA_PARAMS   *pDma,
    U32              *pSglAddress,
    BOOLEAN          *pbBits64
    )
{
    int          rc;
    U8           SizeDescr;
    U32          i;
    U32          offset;
    U32          BusSgl;
    U32          BusSglOriginal;
    U32          SglSize;
    U32          BlockSize;
    U32          LocalAddr;
    U32          TotalDescr;
    U32          BytesRemaining;
    U64          BusAddr;
    BOOLEAN      bDirLocalToPci;
    PLX_UINT_PTR VaSgl;
    PLX_UINT_PTR UserVa;


    DebugPrintf(("Building SGL descriptors for buffer...\n"));
    DebugPrintf(("   User VA   : %08lx\n", (PLX_UINT_PTR)pDma->UserVa));
    DebugPrintf(("   Local Addr: %08x\n", pDma->LocalAddr));
    DebugPrintf(("   Size      : %d bytes\n", pDma->ByteCount));
    DebugPrintf(("   Direction : %s\n",
        (pDma->Direction == PLX_DMA_LOC_TO_PCI) ? "Local --> PCI" : "PCI --> Local"
        ));

    // Set default return address
    *pSglAddress = 0;

    // Store buffer page offset
    pdx->DmaInfo[channel].InitialOffset = (U32)(pDma->UserVa & ~PAGE_MASK);

    offset         = pdx->DmaInfo[channel].InitialOffset;
    UserVa         = pDma->UserVa;
    BytesRemaining = pDma->ByteCount;
    TotalDescr     = 0;

    // Count number of user pages
    while (BytesRemaining != 0)
    {
        // Add an I/O buffer
        TotalDescr++;

        if (BytesRemaining <= (PAGE_SIZE - offset))
        {
            BytesRemaining = 0;
        }
        else
        {
            BytesRemaining -= (PAGE_SIZE - offset);
        }

        // Clear offset
        offset = 0;
    }

    DebugPrintf((
        "Allocating %d bytes for user buffer page list (%d pages)...\n",
        (U32)(TotalDescr * sizeof(struct page *)),
        TotalDescr
        ));

    // Allocate memory to store page list
    pdx->DmaInfo[channel].PageList = 
        kmalloc(
            TotalDescr * sizeof(struct page *),
            GFP_KERNEL
            );

    if (pdx->DmaInfo[channel].PageList == NULL)
    {
        DebugPrintf(("ERROR - Unable to allocate memory for list of pages\n"));
        return ApiDmaSglPagesGetError;
    }

    // Store number of pages
    pdx->DmaInfo[channel].NumPages = TotalDescr;

    // Determine & store DMA transfer direction
    if (pDma->Direction == PLX_DMA_LOC_TO_PCI)
    {
        bDirLocalToPci                  = TRUE;
        pdx->DmaInfo[channel].direction = DMA_FROM_DEVICE;
    }
    else
    {
        bDirLocalToPci                  = FALSE;
        pdx->DmaInfo[channel].direction = DMA_TO_DEVICE;
    }

    // Obtain the mmap reader/writer semaphore
    down_read(
        &current->mm->mmap_sem
        );

    // Attempt to lock the user buffer into memory
    rc =
        get_user_pages(
            current,                          // Task performing I/O
            current->mm,                      // The tasks memory-management structure
            UserVa & PAGE_MASK,               // Page-aligned starting address of user buffer
            TotalDescr,                       // Length of the buffer in pages
            bDirLocalToPci,                   // Map for write access (i.e. user app performing a read)?
            0,                                // Do not force an override of page protections
            pdx->DmaInfo[channel].PageList,   // Will contain list of page pointers describing buffer
            NULL                              // Will contain list of associated VMAs
            );

    // Release mmap semaphore
    up_read(
        &current->mm->mmap_sem
        );

    if (rc != TotalDescr)
    {
        if (rc <= 0)
        {
            DebugPrintf(("ERROR - Unable to map user buffer (code=%d)\n", rc));
        }
        else
        {
            DebugPrintf((
                "ERROR - Only able to map %d of %d total pages\n",
                rc, TotalDescr
                ));
        }

        kfree( pdx->DmaInfo[channel].PageList );
        return ApiDmaSglPagesLockError;
    }

    DebugPrintf((
        "Page-locked %d user buffer pages...\n",
        TotalDescr
        ));

    // Default to 32-bit transfer
    *pbBits64 = FALSE;

    /*************************************************************
     * 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 = (#descriptors * descriptor size) + (rounding bytes)
     *
     * 16 or 32 bytes are added to support rounding up to the next
     * 16 or 32-byte boundary, which is a requirement of the hardware.
     ************************************************************/
    // Calculate descriptor size
    if (*pbBits64)
        SizeDescr = 8 * sizeof(U32);
    else
        SizeDescr = 4 * sizeof(U32);

    // Calculate SGL size
    SglSize = (TotalDescr * SizeDescr) + SizeDescr;

    // Check if a previously allocated buffer can be re-used
    if (pdx->DmaInfo[channel].SglBuffer.pKernelVa != NULL)
    {
        if (pdx->DmaInfo[channel].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[channel].SglBuffer
                );

            pdx->DmaInfo[channel].SglBuffer.pKernelVa = NULL;
        }
    }

    // Allocate memory for SGL descriptors if necessary
    if (pdx->DmaInfo[channel].SglBuffer.pKernelVa == NULL)
    {
        DebugPrintf(("Allocating PCI memory for SGL descriptor buffer...\n"));

        // Setup for transfer
        pdx->DmaInfo[channel].SglBuffer.Size = SglSize;

        VaSgl =
            (PLX_UINT_PTR)Plx_dma_buffer_alloc(
                pdx,
                &pdx->DmaInfo[channel].SglBuffer
                );

        if (VaSgl == 0)
        {
            DebugPrintf((
                "ERROR - Unable to allocate %d bytes for %d SGL descriptors\n",
                pdx->DmaInfo[channel].SglBuffer.Size,
                TotalDescr
                ));

            kfree( pdx->DmaInfo[channel].PageList );
            return ApiInsufficientResources;
        }
    }
    else
    {
        VaSgl = (PLX_UINT_PTR)pdx->DmaInfo[channel].SglBuffer.pKernelVa;
    }

    // Prepare for build of SGL
    LocalAddr = pDma->LocalAddr;

    // Get bus physical address of SGL descriptors
    BusSgl = (U32)pdx->DmaInfo[channel].SglBuffer.BusPhysical;

    // Make sure addresses are aligned on next descriptor boundary
    VaSgl  = (VaSgl + (SizeDescr - 1)) & ~((PLX_UINT_PTR)SizeDescr - 1);
    BusSgl = (BusSgl + (SizeDescr - 1)) & ~((PLX_UINT_PTR)SizeDescr - 1);

    // Store the starting address of the SGL for later return
    BusSglOriginal = BusSgl;

    DebugPrintf((
        "Building SGL at %08x (%d descriptors)\n",
        BusSglOriginal, TotalDescr
        ));

    // Store total buffer size
    pdx->DmaInfo[channel].BufferSize = pDma->ByteCount;

    // Set offset of first page
    offset = pdx->DmaInfo[channel].InitialOffset;

    // Initialize bytes remaining
    BytesRemaining = pDma->ByteCount;

    // Build the SGL list
    for (i = 0; i < TotalDescr; i++)
    {
        // Calculate transfer size
        if (BytesRemaining > (PAGE_SIZE - offset))
        {
            BlockSize = PAGE_SIZE - offset;
        }
        else
        {
            BlockSize = BytesRemaining;
        }

        // Get bus address of buffer
        BusAddr =
            Plx_dma_map_page(
                pdx,
                pdx->DmaInfo[channel].PageList[i],
                offset,
                BlockSize,
                pdx->DmaInfo[channel].direction
                );

        // Enable the following to display the parameters of each SGL descriptor
        if (PLX_DEBUG_DISPLAY_SGL_DESCR)
        {
            DebugPrintf((
                "SGL Desc %02d: PCI=%08X  Loc=%08X  Size=%X (%d) bytes\n",
                i, (U32)BusAddr, LocalAddr, BlockSize, BlockSize
                ));
        }

        // Write PCI address in descriptor
        *(((U32*)VaSgl) + SGL_DESC_IDX_PCI_LOW) = PLX_LE_DATA_32( (U32)BusAddr );

        // Write upper 32-bit of 64-bit PCI address in descriptor
        if (*pbBits64)
            *(((U32*)VaSgl) + SGL_DESC_IDX_PCI_HIGH) = PLX_LE_DATA_32( (U32)(BusAddr >> 32) );

        // Write Local address in descriptor
        *(((U32*)VaSgl) + SGL_DESC_IDX_LOC_ADDR) = PLX_LE_DATA_32( LocalAddr );

        // Write transfer count in descriptor
        *(((U32*)VaSgl) + SGL_DESC_IDX_COUNT) = PLX_LE_DATA_32( BlockSize );

        // Adjust byte count
        BytesRemaining -= BlockSize;

        if (BytesRemaining == 0)
        {
            // Write the last descriptor
            *(((U32*)VaSgl) + SGL_DESC_IDX_NEXT_DESC) =
                PLX_LE_DATA_32(
                    (bDirLocalToPci << 3) | (1 << 1) | (1 << 0)
                    );
        }
        else
        {
            // Calculate address of next descriptor
            BusSgl += SizeDescr;

            // Write next descriptor address
            *(((U32*)VaSgl) + SGL_DESC_IDX_NEXT_DESC) =
                PLX_LE_DATA_32(
                    BusSgl | (bDirLocalToPci << 3) | (1 << 0)
                    );

            // Adjust Local address
            if (pdx->DmaInfo[channel].bConstAddrLocal == FALSE)
                LocalAddr += BlockSize;

            // Adjust virtual address to next descriptor
            VaSgl += SizeDescr;

            // Clear offset
            offset = 0;
        }
    }

    // Return the physical address of the SGL
    *pSglAddress = BusSglOriginal;

    return ApiSuccess;
}