Ejemplo n.º 1
0
/*******************************************************************************
 *
 * Function   :  PlxPciPhysicalMemoryAllocate
 *
 * Description:  Allocate physically contiguous page-locked memory
 *
 ******************************************************************************/
PLX_STATUS
PlxPciPhysicalMemoryAllocate(
    DEVICE_EXTENSION *pdx,
    PLX_PHYSICAL_MEM *pPciMem,
    BOOLEAN           bSmallerOk,
    VOID             *pOwner
    )
{
    U32                  DecrementAmount;
    PLX_PHYS_MEM_OBJECT *pMemObject;


    // Initialize buffer information
    pPciMem->UserAddr     = 0;
    pPciMem->PhysicalAddr = 0;
    pPciMem->CpuPhysical  = 0;

    /*******************************************************
     * Verify size
     *
     * A size of 0 is valid because this function may
     * be called to allocate a common buffer of size 0;
     * therefore, the information is reset & return sucess.
     ******************************************************/
    if (pPciMem->Size == 0)
    {
        return ApiSuccess;
    }

    // Allocate memory for new list object
    pMemObject =
        kmalloc(
            sizeof(PLX_PHYS_MEM_OBJECT),
            GFP_KERNEL
            );

    if (pMemObject == NULL)
    {
        DebugPrintf(("ERROR - Memory allocation for list object failed\n"));
        return ApiInsufficientResources;
    }

    // Clear object
    RtlZeroMemory( pMemObject, sizeof(PLX_PHYS_MEM_OBJECT) );

    // Set buffer request size
    pMemObject->Size = pPciMem->Size;

    // Setup amount to reduce on failure
    DecrementAmount = (pPciMem->Size / 10);

    DebugPrintf((
        "Attempt to allocate physical memory (%d Kb)...\n",
        (pPciMem->Size >> 10)
        ));

    do
    {
        // Attempt to allocate the buffer
        pMemObject->pKernelVa =
            Plx_dma_buffer_alloc(
                pdx,
                pMemObject
                );

        if (pMemObject->pKernelVa == NULL)
        {
            // Reduce memory request size if requested
            if (bSmallerOk && (pMemObject->Size > PAGE_SIZE))
            {
                pMemObject->Size -= DecrementAmount;
            }
            else
            {
                // Release the list object
                kfree(
                    pMemObject
                    );

                DebugPrintf(("ERROR - Physical memory allocation failed\n"));

                pPciMem->Size = 0;

                return ApiInsufficientResources;
            }
        }
    }
    while (pMemObject->pKernelVa == NULL);

    // Record buffer owner
    pMemObject->pOwner = pOwner;

    // Assign buffer to device if provided
    if (pOwner != pGbl_DriverObject)
    {
        // Return buffer information
        pPciMem->Size         = pMemObject->Size;
        pPciMem->PhysicalAddr = pMemObject->BusPhysical;
        pPciMem->CpuPhysical  = pMemObject->CpuPhysical;

        // Add buffer object to list
        spin_lock(
            &(pdx->Lock_PhysicalMemList)
            );

        list_add_tail(
            &(pMemObject->ListEntry),
            &(pdx->List_PhysicalMem)
            );

        spin_unlock(
            &(pdx->Lock_PhysicalMemList)
            );
    }
    else
    {
        // Store common buffer information
        pGbl_DriverObject->CommonBuffer = *pMemObject;

        // Release the list object
        kfree(
            pMemObject
            );
    }

    return ApiSuccess;
}
Ejemplo n.º 2
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;
}