blob: d2ce76da1ea43fa088cac03b7569f3ffb599d1ef [file] [log] [blame]
/* adapter_dmabuf.c
*
* Implementation of the DMA Buffer Allocation API.
*/
/*****************************************************************************
* Copyright (c) 2008-2020 by Rambus, Inc. and/or its subsidiaries.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*****************************************************************************/
/*----------------------------------------------------------------------------
* This module implements (provides) the following interface(s):
*/
// DMABuf API
#include "api_dmabuf.h"
// Adapter DMABuf internal API
#include "adapter_dmabuf.h"
/*----------------------------------------------------------------------------
* This module uses (requires) the following interface(s):
*/
// Logging API
#include "log.h"
// Driver Framework DMAResource API
#include "dmares_types.h"
#include "dmares_mgmt.h"
#include "dmares_buf.h"
#include "dmares_addr.h"
// Driver Framework Basic Definitions API
#include "basic_defs.h"
// Driver Framework C Run-Time Library API
#include "clib.h" // memcmp
/*----------------------------------------------------------------------------
* Definitions and macros
*/
/*----------------------------------------------------------------------------
* Local variables
*/
/*----------------------------------------------------------------------------
* DMABuf_NULLHandle
*
*/
const DMABuf_Handle_t DMABuf_NULLHandle = { NULL };
// Initial DMA buffer alignment setting
static int Adapter_DMABuf_Alignment = ADAPTER_DMABUF_ALIGNMENT_INVALID;
/*----------------------------------------------------------------------------
* Adapter_DMABuf_Handle2DMAResourceHandle
*/
DMAResource_Handle_t
Adapter_DMABuf_Handle2DMAResourceHandle(
DMABuf_Handle_t Handle)
{
if (Handle.p == NULL)
{
return NULL;
}
else
{
return (DMAResource_Handle_t)Handle.p;
}
}
/*----------------------------------------------------------------------------
* Adapter_DMAResource_Handle2DMABufHandle
*/
DMABuf_Handle_t
Adapter_DMAResource_Handle2DMABufHandle(
DMAResource_Handle_t Handle)
{
DMABuf_Handle_t DMABuf_Handle;
DMABuf_Handle.p = Handle;
return DMABuf_Handle;
}
/*----------------------------------------------------------------------------
* Adapter_DMAResource_IsForeignAllocated
*/
bool
Adapter_DMAResource_IsForeignAllocated(
DMAResource_Handle_t Handle)
{
DMAResource_Record_t * Rec_p;
Rec_p = DMAResource_Handle2RecordPtr(Handle);
if(!Rec_p)
{
return false;
}
else
{
return (Rec_p->AllocatorRef != 'A' && Rec_p->AllocatorRef != 'R');
}
}
/*----------------------------------------------------------------------------
* Adapter_DMAResource_HostAddr
*/
void *
Adapter_DMAResource_HostAddr(
DMAResource_Handle_t Handle)
{
DMAResource_AddrPair_t HostAddr;
DMAResource_Translate(Handle, DMARES_DOMAIN_HOST, &HostAddr);
return HostAddr.Address_p;
}
/*----------------------------------------------------------------------------
* Adapter_DMAResource_IsSubRangeOf
*
* Return true if the address range defined by Handle1 is
* within the address range defined by Handle2.
*/
bool
Adapter_DMAResource_IsSubRangeOf(
const DMAResource_Handle_t Handle1,
const DMAResource_Handle_t Handle2)
{
DMAResource_AddrPair_t AddrPair1, AddrPair2;
DMAResource_Translate(Handle1, DMARES_DOMAIN_HOST, &AddrPair1);
DMAResource_Translate(Handle2, DMARES_DOMAIN_HOST, &AddrPair2);
if (AddrPair1.Domain == AddrPair2.Domain)
{
const uint8_t * Addr1 = AddrPair1.Address_p;
const uint8_t * Addr2 = AddrPair2.Address_p;
const DMAResource_Record_t * const Rec1_p =
DMAResource_Handle2RecordPtr(Handle1);
const DMAResource_Record_t * const Rec2_p =
DMAResource_Handle2RecordPtr(Handle2);
if ((Rec1_p->Props.Size <= Rec2_p->Props.Size) &&
(Addr2 <= Addr1) &&
((Addr1 + Rec1_p->Props.Size) <= (Addr2 + Rec2_p->Props.Size)))
{
return true;
}
}
return false;
}
/*----------------------------------------------------------------------------
* Adapter_DMAResource_Alignment_Set
*/
void
Adapter_DMAResource_Alignment_Set(
const int Alignment)
{
Adapter_DMABuf_Alignment = Alignment;
return;
}
/*----------------------------------------------------------------------------
* Adapter_DMAResource_Alignment_Get
*/
int
Adapter_DMAResource_Alignment_Get(void)
{
return Adapter_DMABuf_Alignment;
}
/*----------------------------------------------------------------------------
* DMABuf_Handle_IsSame
*/
bool
DMABuf_Handle_IsSame(
const DMABuf_Handle_t * const Handle1_p,
const DMABuf_Handle_t * const Handle2_p)
{
if (memcmp(Handle1_p, Handle2_p, sizeof(DMABuf_Handle_t)) == 0)
{
return true;
}
return false;
}
/*----------------------------------------------------------------------------
* DMABuf_Alloc
*/
DMABuf_Status_t
DMABuf_Alloc(
const DMABuf_Properties_t RequestedProperties,
DMABuf_HostAddress_t * const Buffer_p,
DMABuf_Handle_t * const Handle_p)
{
DMAResource_Handle_t DMAHandle;
DMAResource_AddrPair_t AddrPair;
DMAResource_Properties_t ActualProperties;
ZEROINIT(AddrPair);
ZEROINIT(ActualProperties);
if (Handle_p == NULL ||
Buffer_p == NULL)
{
return DMABUF_ERROR_BAD_ARGUMENT;
}
// initialize the output parameters
Handle_p->p = NULL;
Buffer_p->p = NULL;
ActualProperties.Size = RequestedProperties.Size;
ActualProperties.Bank = RequestedProperties.Bank;
ActualProperties.fCached = RequestedProperties.fCached;
if (Adapter_DMABuf_Alignment != ADAPTER_DMABUF_ALIGNMENT_INVALID &&
RequestedProperties.Alignment < Adapter_DMABuf_Alignment)
ActualProperties.Alignment = Adapter_DMABuf_Alignment;
else
ActualProperties.Alignment = RequestedProperties.Alignment;
if( !DMAResource_Alloc(ActualProperties,&AddrPair,&DMAHandle) )
{
// set the output parameters
Handle_p->p = (void*)DMAHandle;
Buffer_p->p = AddrPair.Address_p;
LOG_INFO("DMABuf_Alloc: allocated handle=%p, host addr=%p, "
"alignment requested/actual %d/%d, "
"bank requested %d, cached requested %d\n",
Handle_p->p,
Buffer_p->p,
RequestedProperties.Alignment,
ActualProperties.Alignment,
RequestedProperties.Bank,
RequestedProperties.fCached);
return DMABUF_STATUS_OK;
}
else
{
return DMABUF_ERROR_OUT_OF_MEMORY;
}
}
DMABuf_Status_t
DMABuf_Particle_Alloc(
const DMABuf_Properties_t RequestedProperties,
dma_addr_t DmaAddress,
DMABuf_HostAddress_t * const Buffer_p,
DMABuf_Handle_t * const Handle_p)
{
DMAResource_Handle_t DMAHandle;
DMAResource_AddrPair_t AddrPair;
DMAResource_Properties_t ActualProperties;
ZEROINIT(AddrPair);
ZEROINIT(ActualProperties);
if (Handle_p == NULL ||
Buffer_p == NULL)
{
return DMABUF_ERROR_BAD_ARGUMENT;
}
// initialize the output parameters
Handle_p->p = NULL;
Buffer_p->p = NULL;
ActualProperties.Size = RequestedProperties.Size;
ActualProperties.Bank = RequestedProperties.Bank;
ActualProperties.fCached = RequestedProperties.fCached;
if (Adapter_DMABuf_Alignment != ADAPTER_DMABUF_ALIGNMENT_INVALID &&
RequestedProperties.Alignment < Adapter_DMABuf_Alignment)
ActualProperties.Alignment = Adapter_DMABuf_Alignment;
else
ActualProperties.Alignment = RequestedProperties.Alignment;
if(!DMAResource_SG_Alloc(ActualProperties, DmaAddress, &AddrPair,&DMAHandle))
{
// set the output parameters
Handle_p->p = (void*)DMAHandle;
Buffer_p->p = AddrPair.Address_p;
LOG_INFO("DMABuf_Alloc: allocated handle=%p, host addr=%p, "
"alignment requested/actual %d/%d, "
"bank requested %d, cached requested %d\n",
Handle_p->p,
Buffer_p->p,
RequestedProperties.Alignment,
ActualProperties.Alignment,
RequestedProperties.Bank,
RequestedProperties.fCached);
return DMABUF_STATUS_OK;
}
else
{
return DMABUF_ERROR_OUT_OF_MEMORY;
}
}
/*----------------------------------------------------------------------------
* DMABuf_Register
*/
DMABuf_Status_t
DMABuf_Register(
const DMABuf_Properties_t RequestedProperties,
void * Buffer_p,
void * Alternative_p,
const char AllocatorRef,
DMABuf_Handle_t * const Handle_p)
{
DMAResource_Handle_t DMAHandle;
char ActualAllocator;
DMAResource_AddrPair_t AddrPair;
DMAResource_Properties_t ActualProperties;
ZEROINIT(AddrPair);
ZEROINIT(ActualProperties);
if (Handle_p == NULL ||
Buffer_p == NULL)
{
return DMABUF_ERROR_BAD_ARGUMENT;
}
// initialize the output parameter
Handle_p->p = NULL;
ActualProperties.Size = RequestedProperties.Size;
ActualProperties.Bank = RequestedProperties.Bank;
ActualProperties.fCached = RequestedProperties.fCached;
if (Adapter_DMABuf_Alignment != ADAPTER_DMABUF_ALIGNMENT_INVALID &&
RequestedProperties.Alignment < Adapter_DMABuf_Alignment)
ActualProperties.Alignment = Adapter_DMABuf_Alignment;
else
ActualProperties.Alignment = RequestedProperties.Alignment;
ActualAllocator = AllocatorRef;
if( AllocatorRef == 'k' || AllocatorRef == 'N' || AllocatorRef == 'R'
|| AllocatorRef == 'C')
{
// 'N' is used to register buffers that do not need to be DMA-safe.
// 'R' is used to register (subranges of) buffers that are already
// allocated with DMAResource_Alloc()/DMABuf_Alloc().
// 'k' is supported for Linux kmalloc() allocator only,
// e.g. AllocatorRef = 'k' for streaming DMA mappings
// 'C' is supported for coherent buffers.
AddrPair.Domain = DMARES_DOMAIN_HOST;
AddrPair.Address_p = Buffer_p;
}
else if( AllocatorRef == 0 )
{
// Linux kmalloc() allocator is used,
// e.g. AllocatorRef = 'k' for streaming DMA mappings
ActualAllocator = 'k';
AddrPair.Domain = DMARES_DOMAIN_HOST;
AddrPair.Address_p = Buffer_p;
}
else
{
return DMABUF_ERROR_BAD_ARGUMENT;
}
if( DMAResource_CheckAndRegister(ActualProperties,AddrPair,
ActualAllocator,&DMAHandle) == 0 )
{
if( ActualAllocator == 'C' )
{
// Add bus address for the resource for AllocatorRef = 'C'
AddrPair.Domain = DMARES_DOMAIN_BUS;
AddrPair.Address_p = Alternative_p;
DMAResource_AddPair(DMAHandle,AddrPair);
}
// set the output parameters
Handle_p->p = (void*)DMAHandle;
LOG_INFO("DMABuf_Register: registered handle=%p, host addr=%p, "
"allocator=%d, alignment requested/actual %d/%d, "
"bank requested %d, cached requested %d\n",
Handle_p->p,
Buffer_p,
AllocatorRef,
RequestedProperties.Alignment,
ActualProperties.Alignment,
RequestedProperties.Bank,
RequestedProperties.fCached);
return DMABUF_STATUS_OK;
}
else
{
return DMABUF_ERROR_OUT_OF_MEMORY;
}
}
/*----------------------------------------------------------------------------
* DMABuf_Release
*/
DMABuf_Status_t
DMABuf_Release(
DMABuf_Handle_t Handle)
{
DMAResource_Handle_t DMAHandle =
Adapter_DMABuf_Handle2DMAResourceHandle(Handle);
LOG_INFO("DMABuf_Release: handle to release=%p\n",Handle.p);
if( DMAResource_Release(DMAHandle) == 0 )
{
return DMABUF_STATUS_OK;
}
else
{
return DMABUF_ERROR_INVALID_HANDLE;
}
}
DMABuf_Status_t
DMABuf_Particle_Release(
DMABuf_Handle_t Handle)
{
DMAResource_Handle_t DMAHandle =
Adapter_DMABuf_Handle2DMAResourceHandle(Handle);
LOG_INFO("DMABuf_Particle_Release: handle to release=%p\n",Handle.p);
if( DMAResource_SG_Release(DMAHandle) == 0 )
{
return DMABUF_STATUS_OK;
}
else
{
return DMABUF_ERROR_INVALID_HANDLE;
}
}
/* end of file adapter_dmabuf.c */