/******************************************************************************* * * 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; }
/******************************************************************************* * * 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( ¤t->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( ¤t->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; }