mirror of https://github.com/F-Stack/f-stack.git
985 lines
34 KiB
C
985 lines
34 KiB
C
/*
|
|
* Copyright 2008-2012 Freescale Semiconductor Inc.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
* * Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* * Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* * Neither the name of Freescale Semiconductor nor the
|
|
* names of its contributors may be used to endorse or promote products
|
|
* derived from this software without specific prior written permission.
|
|
*
|
|
*
|
|
* ALTERNATIVELY, this software may be distributed under the terms of the
|
|
* GNU General Public License ("GPL") as published by the Free Software
|
|
* Foundation, either version 2 of that License or (at your option) any
|
|
* later version.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY
|
|
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
* DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY
|
|
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
|
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
|
|
/******************************************************************************
|
|
@File fm_replic.c
|
|
|
|
@Description FM frame replicator
|
|
*//***************************************************************************/
|
|
#include "std_ext.h"
|
|
#include "error_ext.h"
|
|
#include "string_ext.h"
|
|
#include "debug_ext.h"
|
|
#include "fm_pcd_ext.h"
|
|
#include "fm_muram_ext.h"
|
|
#include "fm_common.h"
|
|
#include "fm_hc.h"
|
|
#include "fm_replic.h"
|
|
#include "fm_cc.h"
|
|
#include "list_ext.h"
|
|
|
|
|
|
/****************************************/
|
|
/* static functions */
|
|
/****************************************/
|
|
static uint8_t GetMemberPosition(t_FmPcdFrmReplicGroup *p_ReplicGroup,
|
|
uint32_t memberIndex,
|
|
bool isAddOperation)
|
|
{
|
|
uint8_t memberPosition;
|
|
uint32_t lastMemberIndex;
|
|
|
|
ASSERT_COND(p_ReplicGroup);
|
|
|
|
/* the last member index is different between add and remove operation -
|
|
in case of remove - this is exactly the last member index
|
|
in case of add - this is the last member index + 1 - e.g.
|
|
if we have 4 members, the index of the actual last member is 3(because the
|
|
index starts from 0) therefore in order to add a new member as the last
|
|
member we shall use memberIndex = 4 and not 3
|
|
*/
|
|
if (isAddOperation)
|
|
lastMemberIndex = p_ReplicGroup->numOfEntries;
|
|
else
|
|
lastMemberIndex = p_ReplicGroup->numOfEntries-1;
|
|
|
|
/* last */
|
|
if (memberIndex == lastMemberIndex)
|
|
memberPosition = FRM_REPLIC_LAST_MEMBER_INDEX;
|
|
else
|
|
{
|
|
/* first */
|
|
if (memberIndex == 0)
|
|
memberPosition = FRM_REPLIC_FIRST_MEMBER_INDEX;
|
|
else
|
|
{
|
|
/* middle */
|
|
ASSERT_COND(memberIndex < lastMemberIndex);
|
|
memberPosition = FRM_REPLIC_MIDDLE_MEMBER_INDEX;
|
|
}
|
|
}
|
|
return memberPosition;
|
|
}
|
|
|
|
static t_Error MemberCheckParams(t_Handle h_FmPcd,
|
|
t_FmPcdCcNextEngineParams *p_MemberParams)
|
|
{
|
|
t_Error err;
|
|
|
|
|
|
if ((p_MemberParams->nextEngine != e_FM_PCD_DONE) &&
|
|
(p_MemberParams->nextEngine != e_FM_PCD_KG) &&
|
|
(p_MemberParams->nextEngine != e_FM_PCD_PLCR))
|
|
RETURN_ERROR(MAJOR, E_NOT_SUPPORTED, ("Next engine of a member should be MatchTable(cc) or Done or Policer"));
|
|
|
|
/* check the regular parameters of the next engine */
|
|
err = ValidateNextEngineParams(h_FmPcd, p_MemberParams, e_FM_PCD_CC_STATS_MODE_NONE);
|
|
if (err)
|
|
RETURN_ERROR(MAJOR, err, ("member next engine parameters"));
|
|
|
|
return E_OK;
|
|
}
|
|
|
|
static t_Error CheckParams(t_Handle h_FmPcd,
|
|
t_FmPcdFrmReplicGroupParams *p_ReplicGroupParam)
|
|
{
|
|
int i;
|
|
t_Error err;
|
|
|
|
/* check that max num of entries is at least 2 */
|
|
if (!IN_RANGE(2, p_ReplicGroupParam->maxNumOfEntries, FM_PCD_FRM_REPLIC_MAX_NUM_OF_ENTRIES))
|
|
RETURN_ERROR(MAJOR, E_NOT_IN_RANGE, ("maxNumOfEntries in the frame replicator parameters should be 2-%d",FM_PCD_FRM_REPLIC_MAX_NUM_OF_ENTRIES));
|
|
|
|
/* check that number of entries is greater than zero */
|
|
if (!p_ReplicGroupParam->numOfEntries)
|
|
RETURN_ERROR(MAJOR, E_INVALID_VALUE, ("numOFEntries in the frame replicator group should be greater than zero"));
|
|
|
|
/* check that max num of entries is equal or greater than number of entries */
|
|
if (p_ReplicGroupParam->maxNumOfEntries < p_ReplicGroupParam->numOfEntries)
|
|
RETURN_ERROR(MAJOR, E_INVALID_VALUE, ("maxNumOfEntries should be equal or greater than numOfEntries"));
|
|
|
|
for (i=0; i<p_ReplicGroupParam->numOfEntries; i++)
|
|
{
|
|
err = MemberCheckParams(h_FmPcd, &p_ReplicGroupParam->nextEngineParams[i]);
|
|
if (err)
|
|
RETURN_ERROR(MAJOR, err, ("member check parameters"));
|
|
}
|
|
return E_OK;
|
|
}
|
|
|
|
static t_FmPcdFrmReplicMember *GetAvailableMember(t_FmPcdFrmReplicGroup *p_ReplicGroup)
|
|
{
|
|
t_FmPcdFrmReplicMember *p_ReplicMember = NULL;
|
|
t_List *p_Next;
|
|
|
|
if (!LIST_IsEmpty(&p_ReplicGroup->availableMembersList))
|
|
{
|
|
p_Next = LIST_FIRST(&p_ReplicGroup->availableMembersList);
|
|
p_ReplicMember = LIST_OBJECT(p_Next, t_FmPcdFrmReplicMember, node);
|
|
ASSERT_COND(p_ReplicMember);
|
|
LIST_DelAndInit(p_Next);
|
|
}
|
|
return p_ReplicMember;
|
|
}
|
|
|
|
static void PutAvailableMember(t_FmPcdFrmReplicGroup *p_ReplicGroup,
|
|
t_FmPcdFrmReplicMember *p_ReplicMember)
|
|
{
|
|
LIST_AddToTail(&p_ReplicMember->node, &p_ReplicGroup->availableMembersList);
|
|
}
|
|
|
|
static void AddMemberToList(t_FmPcdFrmReplicGroup *p_ReplicGroup,
|
|
t_FmPcdFrmReplicMember *p_CurrentMember,
|
|
t_List *p_ListHead)
|
|
{
|
|
LIST_Add(&p_CurrentMember->node, p_ListHead);
|
|
|
|
p_ReplicGroup->numOfEntries++;
|
|
}
|
|
|
|
static void RemoveMemberFromList(t_FmPcdFrmReplicGroup *p_ReplicGroup,
|
|
t_FmPcdFrmReplicMember *p_CurrentMember)
|
|
{
|
|
ASSERT_COND(p_ReplicGroup->numOfEntries);
|
|
LIST_DelAndInit(&p_CurrentMember->node);
|
|
p_ReplicGroup->numOfEntries--;
|
|
}
|
|
|
|
static void LinkSourceToMember(t_FmPcdFrmReplicGroup *p_ReplicGroup,
|
|
t_AdOfTypeContLookup *p_SourceTd,
|
|
t_FmPcdFrmReplicMember *p_ReplicMember)
|
|
{
|
|
t_FmPcd *p_FmPcd;
|
|
|
|
ASSERT_COND(p_SourceTd);
|
|
ASSERT_COND(p_ReplicMember);
|
|
ASSERT_COND(p_ReplicGroup);
|
|
ASSERT_COND(p_ReplicGroup->h_FmPcd);
|
|
|
|
/* Link the first member in the group to the source TD */
|
|
p_FmPcd = p_ReplicGroup->h_FmPcd;
|
|
|
|
WRITE_UINT32(p_SourceTd->matchTblPtr,
|
|
(uint32_t)(XX_VirtToPhys(p_ReplicMember->p_MemberAd) -
|
|
p_FmPcd->physicalMuramBase));
|
|
}
|
|
|
|
static void LinkMemberToMember(t_FmPcdFrmReplicGroup *p_ReplicGroup,
|
|
t_FmPcdFrmReplicMember *p_CurrentMember,
|
|
t_FmPcdFrmReplicMember *p_NextMember)
|
|
{
|
|
t_AdOfTypeResult *p_CurrReplicAd = (t_AdOfTypeResult*)p_CurrentMember->p_MemberAd;
|
|
t_AdOfTypeResult *p_NextReplicAd = NULL;
|
|
t_FmPcd *p_FmPcd;
|
|
uint32_t offset = 0;
|
|
|
|
/* Check if the next member exists or it's NULL (- means that this is the last member) */
|
|
if (p_NextMember)
|
|
{
|
|
p_NextReplicAd = (t_AdOfTypeResult*)p_NextMember->p_MemberAd;
|
|
p_FmPcd = p_ReplicGroup->h_FmPcd;
|
|
offset = (XX_VirtToPhys(p_NextReplicAd) - (p_FmPcd->physicalMuramBase));
|
|
offset = ((offset>>NEXT_FRM_REPLIC_ADDR_SHIFT)<< NEXT_FRM_REPLIC_MEMBER_INDEX_SHIFT);
|
|
}
|
|
|
|
/* link the current AD to point to the AD of the next member */
|
|
WRITE_UINT32(p_CurrReplicAd->res, offset);
|
|
}
|
|
|
|
static t_Error ModifyDescriptor(t_FmPcdFrmReplicGroup *p_ReplicGroup,
|
|
void *p_OldDescriptor,
|
|
void *p_NewDescriptor)
|
|
{
|
|
t_Handle h_Hc;
|
|
t_Error err;
|
|
t_FmPcd *p_FmPcd;
|
|
|
|
ASSERT_COND(p_ReplicGroup);
|
|
ASSERT_COND(p_ReplicGroup->h_FmPcd);
|
|
ASSERT_COND(p_OldDescriptor);
|
|
ASSERT_COND(p_NewDescriptor);
|
|
|
|
p_FmPcd = p_ReplicGroup->h_FmPcd;
|
|
h_Hc = FmPcdGetHcHandle(p_FmPcd);
|
|
if (!h_Hc)
|
|
RETURN_ERROR(MAJOR, E_INVALID_HANDLE, ("Host command"));
|
|
|
|
err = FmHcPcdCcDoDynamicChange(h_Hc,
|
|
(uint32_t)(XX_VirtToPhys(p_OldDescriptor) - p_FmPcd->physicalMuramBase),
|
|
(uint32_t)(XX_VirtToPhys(p_NewDescriptor) - p_FmPcd->physicalMuramBase));
|
|
if (err)
|
|
RETURN_ERROR(MAJOR, err, ("Dynamic change host command"));
|
|
|
|
return E_OK;
|
|
}
|
|
|
|
static void FillReplicAdOfTypeResult(void *p_ReplicAd, bool last)
|
|
{
|
|
t_AdOfTypeResult *p_CurrReplicAd = (t_AdOfTypeResult*)p_ReplicAd;
|
|
uint32_t tmp;
|
|
|
|
tmp = GET_UINT32(p_CurrReplicAd->plcrProfile);
|
|
if (last)
|
|
/* clear the NL bit in case it's the last member in the group*/
|
|
WRITE_UINT32(p_CurrReplicAd->plcrProfile,(tmp & ~FRM_REPLIC_NL_BIT));
|
|
else
|
|
/* set the NL bit in case it's not the last member in the group */
|
|
WRITE_UINT32(p_CurrReplicAd->plcrProfile, (tmp |FRM_REPLIC_NL_BIT));
|
|
|
|
/* set FR bit in the action descriptor */
|
|
tmp = GET_UINT32(p_CurrReplicAd->nia);
|
|
WRITE_UINT32(p_CurrReplicAd->nia,
|
|
(tmp | FRM_REPLIC_FR_BIT | FM_PCD_AD_RESULT_EXTENDED_MODE ));
|
|
}
|
|
|
|
static void BuildSourceTd(void *p_Ad)
|
|
{
|
|
t_AdOfTypeContLookup *p_SourceTd;
|
|
|
|
ASSERT_COND(p_Ad);
|
|
|
|
p_SourceTd = (t_AdOfTypeContLookup *)p_Ad;
|
|
|
|
IOMemSet32((uint8_t*)p_SourceTd, 0, FM_PCD_CC_AD_ENTRY_SIZE);
|
|
|
|
/* initialize the source table descriptor */
|
|
WRITE_UINT32(p_SourceTd->ccAdBase, FM_PCD_AD_CONT_LOOKUP_TYPE);
|
|
WRITE_UINT32(p_SourceTd->pcAndOffsets, FRM_REPLIC_SOURCE_TD_OPCODE);
|
|
}
|
|
|
|
static t_Error BuildShadowAndModifyDescriptor(t_FmPcdFrmReplicGroup *p_ReplicGroup,
|
|
t_FmPcdFrmReplicMember *p_NextMember,
|
|
t_FmPcdFrmReplicMember *p_CurrentMember,
|
|
bool sourceDescriptor,
|
|
bool last)
|
|
{
|
|
t_FmPcd *p_FmPcd;
|
|
t_FmPcdFrmReplicMember shadowMember;
|
|
t_Error err;
|
|
|
|
ASSERT_COND(p_ReplicGroup);
|
|
ASSERT_COND(p_ReplicGroup->h_FmPcd);
|
|
|
|
p_FmPcd = p_ReplicGroup->h_FmPcd;
|
|
ASSERT_COND(p_FmPcd->p_CcShadow);
|
|
|
|
if (!TRY_LOCK(p_FmPcd->h_ShadowSpinlock, &p_FmPcd->shadowLock))
|
|
return ERROR_CODE(E_BUSY);
|
|
|
|
if (sourceDescriptor)
|
|
{
|
|
BuildSourceTd(p_FmPcd->p_CcShadow);
|
|
LinkSourceToMember(p_ReplicGroup, p_FmPcd->p_CcShadow, p_NextMember);
|
|
|
|
/* Modify the source table descriptor according to the prepared shadow descriptor */
|
|
err = ModifyDescriptor(p_ReplicGroup,
|
|
p_ReplicGroup->p_SourceTd,
|
|
p_FmPcd->p_CcShadow/* new prepared source td */);
|
|
|
|
RELEASE_LOCK(p_FmPcd->shadowLock);
|
|
if (err)
|
|
RETURN_ERROR(MAJOR, err, ("Modify source Descriptor in BuildShadowAndModifyDescriptor"));
|
|
|
|
}
|
|
else
|
|
{
|
|
IO2IOCpy32(p_FmPcd->p_CcShadow,
|
|
p_CurrentMember->p_MemberAd,
|
|
FM_PCD_CC_AD_ENTRY_SIZE);
|
|
|
|
/* update the last bit in the shadow ad */
|
|
FillReplicAdOfTypeResult(p_FmPcd->p_CcShadow, last);
|
|
|
|
shadowMember.p_MemberAd = p_FmPcd->p_CcShadow;
|
|
|
|
/* update the next FR member index */
|
|
LinkMemberToMember(p_ReplicGroup, &shadowMember, p_NextMember);
|
|
|
|
/* Modify the next member according to the prepared shadow descriptor */
|
|
err = ModifyDescriptor(p_ReplicGroup,
|
|
p_CurrentMember->p_MemberAd,
|
|
p_FmPcd->p_CcShadow);
|
|
|
|
RELEASE_LOCK(p_FmPcd->shadowLock);
|
|
if (err)
|
|
RETURN_ERROR(MAJOR, err, ("Modify Descriptor in BuildShadowAndModifyDescriptor"));
|
|
}
|
|
|
|
|
|
return E_OK;
|
|
}
|
|
|
|
static t_FmPcdFrmReplicMember* GetMemberByIndex(t_FmPcdFrmReplicGroup *p_ReplicGroup,
|
|
uint16_t memberIndex)
|
|
{
|
|
int i=0;
|
|
t_List *p_Pos;
|
|
t_FmPcdFrmReplicMember *p_Member = NULL;
|
|
|
|
LIST_FOR_EACH(p_Pos, &p_ReplicGroup->membersList)
|
|
{
|
|
if (i == memberIndex)
|
|
{
|
|
p_Member = LIST_OBJECT(p_Pos, t_FmPcdFrmReplicMember, node);
|
|
return p_Member;
|
|
}
|
|
i++;
|
|
}
|
|
return p_Member;
|
|
}
|
|
|
|
static t_Error AllocMember(t_FmPcdFrmReplicGroup *p_ReplicGroup)
|
|
{
|
|
t_FmPcdFrmReplicMember *p_CurrentMember;
|
|
t_Handle h_Muram;
|
|
|
|
ASSERT_COND(p_ReplicGroup);
|
|
|
|
h_Muram = FmPcdGetMuramHandle(p_ReplicGroup->h_FmPcd);
|
|
ASSERT_COND(h_Muram);
|
|
|
|
/* Initialize an internal structure of a member to add to the available members list */
|
|
p_CurrentMember = (t_FmPcdFrmReplicMember *)XX_Malloc(sizeof(t_FmPcdFrmReplicMember));
|
|
if (!p_CurrentMember)
|
|
RETURN_ERROR(MAJOR, E_NO_MEMORY, ("Frame replicator member"));
|
|
|
|
memset(p_CurrentMember, 0 ,sizeof(t_FmPcdFrmReplicMember));
|
|
|
|
/* Allocate the member AD */
|
|
p_CurrentMember->p_MemberAd =
|
|
(t_AdOfTypeResult*)FM_MURAM_AllocMem(h_Muram,
|
|
FM_PCD_CC_AD_ENTRY_SIZE,
|
|
FM_PCD_CC_AD_TABLE_ALIGN);
|
|
if (!p_CurrentMember->p_MemberAd)
|
|
{
|
|
XX_Free(p_CurrentMember);
|
|
RETURN_ERROR(MAJOR, E_NO_MEMORY, ("member AD table"));
|
|
}
|
|
IOMemSet32((uint8_t*)p_CurrentMember->p_MemberAd, 0, FM_PCD_CC_AD_ENTRY_SIZE);
|
|
|
|
/* Add the new member to the available members list */
|
|
LIST_AddToTail(&p_CurrentMember->node, &(p_ReplicGroup->availableMembersList));
|
|
|
|
return E_OK;
|
|
}
|
|
|
|
static t_FmPcdFrmReplicMember* InitMember(t_FmPcdFrmReplicGroup *p_ReplicGroup,
|
|
t_FmPcdCcNextEngineParams *p_MemberParams,
|
|
bool last)
|
|
{
|
|
t_FmPcdFrmReplicMember *p_CurrentMember = NULL;
|
|
|
|
ASSERT_COND(p_ReplicGroup);
|
|
|
|
/* Get an available member from the internal members list */
|
|
p_CurrentMember = GetAvailableMember(p_ReplicGroup);
|
|
if (!p_CurrentMember)
|
|
{
|
|
REPORT_ERROR(MAJOR, E_NOT_FOUND, ("Available member"));
|
|
return NULL;
|
|
}
|
|
p_CurrentMember->h_Manip = NULL;
|
|
|
|
/* clear the Ad of the new member */
|
|
IOMemSet32((uint8_t*)p_CurrentMember->p_MemberAd, 0, FM_PCD_CC_AD_ENTRY_SIZE);
|
|
|
|
INIT_LIST(&p_CurrentMember->node);
|
|
|
|
/* Initialize the Ad of the member */
|
|
NextStepAd(p_CurrentMember->p_MemberAd,
|
|
NULL,
|
|
p_MemberParams,
|
|
p_ReplicGroup->h_FmPcd);
|
|
|
|
/* save Manip handle (for free needs) */
|
|
if (p_MemberParams->h_Manip)
|
|
p_CurrentMember->h_Manip = p_MemberParams->h_Manip;
|
|
|
|
/* Initialize the relevant frame replicator fields in the AD */
|
|
FillReplicAdOfTypeResult(p_CurrentMember->p_MemberAd, last);
|
|
|
|
return p_CurrentMember;
|
|
}
|
|
|
|
static void FreeMember(t_FmPcdFrmReplicGroup *p_ReplicGroup,
|
|
t_FmPcdFrmReplicMember *p_Member)
|
|
{
|
|
/* Note: Can't free the member AD just returns the member to the available
|
|
member list - therefore only memset the AD */
|
|
|
|
/* zero the AD */
|
|
IOMemSet32(p_Member->p_MemberAd, 0, FM_PCD_CC_AD_ENTRY_SIZE);
|
|
|
|
|
|
/* return the member to the available members list */
|
|
PutAvailableMember(p_ReplicGroup, p_Member);
|
|
}
|
|
|
|
static t_Error RemoveMember(t_FmPcdFrmReplicGroup *p_ReplicGroup,
|
|
uint16_t memberIndex)
|
|
{
|
|
t_FmPcd *p_FmPcd = NULL;
|
|
t_FmPcdFrmReplicMember *p_CurrentMember = NULL, *p_PreviousMember = NULL, *p_NextMember = NULL;
|
|
t_Error err;
|
|
uint8_t memberPosition;
|
|
|
|
p_FmPcd = p_ReplicGroup->h_FmPcd;
|
|
ASSERT_COND(p_FmPcd);
|
|
UNUSED(p_FmPcd);
|
|
|
|
p_CurrentMember = GetMemberByIndex(p_ReplicGroup, memberIndex);
|
|
ASSERT_COND(p_CurrentMember);
|
|
|
|
/* determine the member position in the group */
|
|
memberPosition = GetMemberPosition(p_ReplicGroup,
|
|
memberIndex,
|
|
FALSE/*remove operation*/);
|
|
|
|
switch (memberPosition)
|
|
{
|
|
case FRM_REPLIC_FIRST_MEMBER_INDEX:
|
|
p_NextMember = GetMemberByIndex(p_ReplicGroup, (uint16_t)(memberIndex+1));
|
|
ASSERT_COND(p_NextMember);
|
|
|
|
/* update the source td itself by using a host command */
|
|
err = BuildShadowAndModifyDescriptor(p_ReplicGroup,
|
|
p_NextMember,
|
|
NULL,
|
|
TRUE/*sourceDescriptor*/,
|
|
FALSE/*last*/);
|
|
break;
|
|
|
|
case FRM_REPLIC_MIDDLE_MEMBER_INDEX:
|
|
p_PreviousMember = GetMemberByIndex(p_ReplicGroup, (uint16_t)(memberIndex-1));
|
|
ASSERT_COND(p_PreviousMember);
|
|
|
|
p_NextMember = GetMemberByIndex(p_ReplicGroup, (uint16_t)(memberIndex+1));
|
|
ASSERT_COND(p_NextMember);
|
|
|
|
err = BuildShadowAndModifyDescriptor(p_ReplicGroup,
|
|
p_NextMember,
|
|
p_PreviousMember,
|
|
FALSE/*sourceDescriptor*/,
|
|
FALSE/*last*/);
|
|
|
|
break;
|
|
|
|
case FRM_REPLIC_LAST_MEMBER_INDEX:
|
|
p_PreviousMember = GetMemberByIndex(p_ReplicGroup, (uint16_t)(memberIndex-1));
|
|
ASSERT_COND(p_PreviousMember);
|
|
|
|
err = BuildShadowAndModifyDescriptor(p_ReplicGroup,
|
|
NULL,
|
|
p_PreviousMember,
|
|
FALSE/*sourceDescriptor*/,
|
|
TRUE/*last*/);
|
|
break;
|
|
|
|
default:
|
|
RETURN_ERROR(MAJOR, E_INVALID_SELECTION, ("member position in remove member"));
|
|
}
|
|
|
|
if (err)
|
|
RETURN_ERROR(MAJOR, err, NO_MSG);
|
|
|
|
if (p_CurrentMember->h_Manip)
|
|
{
|
|
FmPcdManipUpdateOwner(p_CurrentMember->h_Manip, FALSE);
|
|
p_CurrentMember->h_Manip = NULL;
|
|
}
|
|
|
|
/* remove the member from the driver internal members list */
|
|
RemoveMemberFromList(p_ReplicGroup, p_CurrentMember);
|
|
|
|
/* return the member to the available members list */
|
|
FreeMember(p_ReplicGroup, p_CurrentMember);
|
|
|
|
return E_OK;
|
|
}
|
|
|
|
static void DeleteGroup(t_FmPcdFrmReplicGroup *p_ReplicGroup)
|
|
{
|
|
int i, j;
|
|
t_Handle h_Muram;
|
|
t_FmPcdFrmReplicMember *p_Member, *p_CurrentMember;
|
|
|
|
if (p_ReplicGroup)
|
|
{
|
|
ASSERT_COND(p_ReplicGroup->h_FmPcd);
|
|
h_Muram = FmPcdGetMuramHandle(p_ReplicGroup->h_FmPcd);
|
|
ASSERT_COND(h_Muram);
|
|
|
|
/* free the source table descriptor */
|
|
if (p_ReplicGroup->p_SourceTd)
|
|
{
|
|
FM_MURAM_FreeMem(h_Muram, p_ReplicGroup->p_SourceTd);
|
|
p_ReplicGroup->p_SourceTd = NULL;
|
|
}
|
|
|
|
/* Remove all members from the members linked list (hw and sw) and
|
|
return the members to the available members list */
|
|
if (p_ReplicGroup->numOfEntries)
|
|
{
|
|
j = p_ReplicGroup->numOfEntries-1;
|
|
|
|
/* manually removal of the member because there are no owners of
|
|
this group */
|
|
for (i=j; i>=0; i--)
|
|
{
|
|
p_CurrentMember = GetMemberByIndex(p_ReplicGroup, (uint16_t)i/*memberIndex*/);
|
|
ASSERT_COND(p_CurrentMember);
|
|
|
|
if (p_CurrentMember->h_Manip)
|
|
{
|
|
FmPcdManipUpdateOwner(p_CurrentMember->h_Manip, FALSE);
|
|
p_CurrentMember->h_Manip = NULL;
|
|
}
|
|
|
|
/* remove the member from the internal driver members list */
|
|
RemoveMemberFromList(p_ReplicGroup, p_CurrentMember);
|
|
|
|
/* return the member to the available members list */
|
|
FreeMember(p_ReplicGroup, p_CurrentMember);
|
|
}
|
|
}
|
|
|
|
/* Free members AD */
|
|
for (i=0; i<p_ReplicGroup->maxNumOfEntries; i++)
|
|
{
|
|
p_Member = GetAvailableMember(p_ReplicGroup);
|
|
ASSERT_COND(p_Member);
|
|
if (p_Member->p_MemberAd)
|
|
{
|
|
FM_MURAM_FreeMem(h_Muram, p_Member->p_MemberAd);
|
|
p_Member->p_MemberAd = NULL;
|
|
}
|
|
XX_Free(p_Member);
|
|
}
|
|
|
|
/* release the group lock */
|
|
if (p_ReplicGroup->p_Lock)
|
|
FmPcdReleaseLock(p_ReplicGroup->h_FmPcd, p_ReplicGroup->p_Lock);
|
|
|
|
/* free the replicator group */
|
|
XX_Free(p_ReplicGroup);
|
|
}
|
|
}
|
|
|
|
|
|
/*****************************************************************************/
|
|
/* Inter-module API routines */
|
|
/*****************************************************************************/
|
|
|
|
/* NOTE: the inter-module routines are locked by cc in case of using them */
|
|
void * FrmReplicGroupGetSourceTableDescriptor(t_Handle h_ReplicGroup)
|
|
{
|
|
t_FmPcdFrmReplicGroup *p_ReplicGroup = (t_FmPcdFrmReplicGroup *)h_ReplicGroup;
|
|
ASSERT_COND(p_ReplicGroup);
|
|
|
|
return (p_ReplicGroup->p_SourceTd);
|
|
}
|
|
|
|
void FrmReplicGroupUpdateAd(t_Handle h_ReplicGroup,
|
|
void *p_Ad,
|
|
t_Handle *h_AdNew)
|
|
{
|
|
t_FmPcdFrmReplicGroup *p_ReplicGroup = (t_FmPcdFrmReplicGroup *)h_ReplicGroup;
|
|
t_AdOfTypeResult *p_AdResult = (t_AdOfTypeResult*)p_Ad;
|
|
t_FmPcd *p_FmPcd;
|
|
|
|
ASSERT_COND(p_ReplicGroup);
|
|
p_FmPcd = p_ReplicGroup->h_FmPcd;
|
|
|
|
/* build a bypass ad */
|
|
WRITE_UINT32(p_AdResult->fqid, FM_PCD_AD_BYPASS_TYPE |
|
|
(uint32_t)((XX_VirtToPhys(p_ReplicGroup->p_SourceTd)) - p_FmPcd->physicalMuramBase));
|
|
|
|
*h_AdNew = NULL;
|
|
}
|
|
|
|
void FrmReplicGroupUpdateOwner(t_Handle h_ReplicGroup,
|
|
bool add)
|
|
{
|
|
t_FmPcdFrmReplicGroup *p_ReplicGroup = (t_FmPcdFrmReplicGroup *)h_ReplicGroup;
|
|
ASSERT_COND(p_ReplicGroup);
|
|
|
|
/* update the group owner counter */
|
|
if (add)
|
|
p_ReplicGroup->owners++;
|
|
else
|
|
{
|
|
ASSERT_COND(p_ReplicGroup->owners);
|
|
p_ReplicGroup->owners--;
|
|
}
|
|
}
|
|
|
|
t_Error FrmReplicGroupTryLock(t_Handle h_ReplicGroup)
|
|
{
|
|
t_FmPcdFrmReplicGroup *p_ReplicGroup = (t_FmPcdFrmReplicGroup *)h_ReplicGroup;
|
|
|
|
ASSERT_COND(h_ReplicGroup);
|
|
|
|
if (FmPcdLockTryLock(p_ReplicGroup->p_Lock))
|
|
return E_OK;
|
|
|
|
return ERROR_CODE(E_BUSY);
|
|
}
|
|
|
|
void FrmReplicGroupUnlock(t_Handle h_ReplicGroup)
|
|
{
|
|
t_FmPcdFrmReplicGroup *p_ReplicGroup = (t_FmPcdFrmReplicGroup *)h_ReplicGroup;
|
|
|
|
ASSERT_COND(h_ReplicGroup);
|
|
|
|
FmPcdLockUnlock(p_ReplicGroup->p_Lock);
|
|
}
|
|
/*********************** End of inter-module routines ************************/
|
|
|
|
|
|
/****************************************/
|
|
/* API Init unit functions */
|
|
/****************************************/
|
|
t_Handle FM_PCD_FrmReplicSetGroup(t_Handle h_FmPcd,
|
|
t_FmPcdFrmReplicGroupParams *p_ReplicGroupParam)
|
|
{
|
|
t_FmPcdFrmReplicGroup *p_ReplicGroup;
|
|
t_FmPcdFrmReplicMember *p_CurrentMember, *p_NextMember = NULL;
|
|
int i;
|
|
t_Error err;
|
|
bool last = FALSE;
|
|
t_Handle h_Muram;
|
|
|
|
SANITY_CHECK_RETURN_VALUE(h_FmPcd, E_INVALID_HANDLE, NULL);
|
|
SANITY_CHECK_RETURN_VALUE(p_ReplicGroupParam, E_INVALID_HANDLE, NULL);
|
|
|
|
if (!FmPcdIsAdvancedOffloadSupported(h_FmPcd))
|
|
{
|
|
REPORT_ERROR(MAJOR, E_INVALID_STATE, ("Advanced-offload must be enabled"));
|
|
return NULL;
|
|
}
|
|
|
|
err = CheckParams(h_FmPcd, p_ReplicGroupParam);
|
|
if (err)
|
|
{
|
|
REPORT_ERROR(MAJOR, err, (NO_MSG));
|
|
return NULL;
|
|
}
|
|
|
|
p_ReplicGroup = (t_FmPcdFrmReplicGroup*)XX_Malloc(sizeof(t_FmPcdFrmReplicGroup));
|
|
if (!p_ReplicGroup)
|
|
{
|
|
REPORT_ERROR(MAJOR, E_NO_MEMORY, ("No memory"));
|
|
return NULL;
|
|
}
|
|
memset(p_ReplicGroup, 0, sizeof(t_FmPcdFrmReplicGroup));
|
|
|
|
/* initialize lists for internal driver use */
|
|
INIT_LIST(&p_ReplicGroup->availableMembersList);
|
|
INIT_LIST(&p_ReplicGroup->membersList);
|
|
|
|
p_ReplicGroup->h_FmPcd = h_FmPcd;
|
|
|
|
h_Muram = FmPcdGetMuramHandle(p_ReplicGroup->h_FmPcd);
|
|
ASSERT_COND(h_Muram);
|
|
|
|
/* initialize the group lock */
|
|
p_ReplicGroup->p_Lock = FmPcdAcquireLock(p_ReplicGroup->h_FmPcd);
|
|
if (!p_ReplicGroup->p_Lock)
|
|
{
|
|
REPORT_ERROR(MAJOR, E_NO_MEMORY, ("Replic group lock"));
|
|
DeleteGroup(p_ReplicGroup);
|
|
return NULL;
|
|
}
|
|
|
|
/* Allocate the frame replicator source table descriptor */
|
|
p_ReplicGroup->p_SourceTd =
|
|
(t_Handle)FM_MURAM_AllocMem(h_Muram,
|
|
FM_PCD_CC_AD_ENTRY_SIZE,
|
|
FM_PCD_CC_AD_TABLE_ALIGN);
|
|
if (!p_ReplicGroup->p_SourceTd)
|
|
{
|
|
REPORT_ERROR(MAJOR, E_NO_MEMORY, ("frame replicator source table descriptor"));
|
|
DeleteGroup(p_ReplicGroup);
|
|
return NULL;
|
|
}
|
|
|
|
/* update the shadow size - required for the host commands */
|
|
err = FmPcdUpdateCcShadow(p_ReplicGroup->h_FmPcd,
|
|
FM_PCD_CC_AD_ENTRY_SIZE,
|
|
FM_PCD_CC_AD_TABLE_ALIGN);
|
|
if (err)
|
|
{
|
|
REPORT_ERROR(MAJOR, err, ("Update CC shadow"));
|
|
DeleteGroup(p_ReplicGroup);
|
|
return NULL;
|
|
}
|
|
|
|
p_ReplicGroup->maxNumOfEntries = p_ReplicGroupParam->maxNumOfEntries;
|
|
|
|
/* Allocate the maximal number of members ADs and Statistics AD for the group
|
|
It prevents allocation of Muram in run-time */
|
|
for (i=0; i<p_ReplicGroup->maxNumOfEntries; i++)
|
|
{
|
|
err = AllocMember(p_ReplicGroup);
|
|
if (err)
|
|
{
|
|
REPORT_ERROR(MAJOR, err, ("allocate a new member"));
|
|
DeleteGroup(p_ReplicGroup);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
/* Initialize the members linked lists:
|
|
(hw - the one that is used by the FMan controller and
|
|
sw - the one that is managed by the driver internally) */
|
|
for (i=(p_ReplicGroupParam->numOfEntries-1); i>=0; i--)
|
|
{
|
|
/* check if this is the last member in the group */
|
|
if (i == (p_ReplicGroupParam->numOfEntries-1))
|
|
last = TRUE;
|
|
else
|
|
last = FALSE;
|
|
|
|
/* Initialize a new member */
|
|
p_CurrentMember = InitMember(p_ReplicGroup,
|
|
&(p_ReplicGroupParam->nextEngineParams[i]),
|
|
last);
|
|
if (!p_CurrentMember)
|
|
{
|
|
REPORT_ERROR(MAJOR, E_INVALID_HANDLE, ("No available member"));
|
|
DeleteGroup(p_ReplicGroup);
|
|
return NULL;
|
|
}
|
|
|
|
/* Build the members group - link two consecutive members in the hw linked list */
|
|
LinkMemberToMember(p_ReplicGroup, p_CurrentMember, p_NextMember);
|
|
|
|
/* update the driver internal members list to be compatible to the hw members linked list */
|
|
AddMemberToList(p_ReplicGroup, p_CurrentMember, &p_ReplicGroup->membersList);
|
|
|
|
p_NextMember = p_CurrentMember;
|
|
}
|
|
|
|
/* initialize the source table descriptor */
|
|
BuildSourceTd(p_ReplicGroup->p_SourceTd);
|
|
|
|
/* link the source table descriptor to point to the first member in the group */
|
|
LinkSourceToMember(p_ReplicGroup, p_ReplicGroup->p_SourceTd, p_NextMember);
|
|
|
|
return p_ReplicGroup;
|
|
}
|
|
|
|
t_Error FM_PCD_FrmReplicDeleteGroup(t_Handle h_ReplicGroup)
|
|
{
|
|
t_FmPcdFrmReplicGroup *p_ReplicGroup = (t_FmPcdFrmReplicGroup *)h_ReplicGroup;
|
|
|
|
SANITY_CHECK_RETURN_ERROR(p_ReplicGroup, E_INVALID_HANDLE);
|
|
|
|
if (p_ReplicGroup->owners)
|
|
RETURN_ERROR(MAJOR,
|
|
E_INVALID_STATE,
|
|
("the group has owners and can't be deleted"));
|
|
|
|
DeleteGroup(p_ReplicGroup);
|
|
|
|
return E_OK;
|
|
}
|
|
|
|
|
|
/*****************************************************************************/
|
|
/* API Run-time Frame replicator Control unit functions */
|
|
/*****************************************************************************/
|
|
t_Error FM_PCD_FrmReplicAddMember(t_Handle h_ReplicGroup,
|
|
uint16_t memberIndex,
|
|
t_FmPcdCcNextEngineParams *p_MemberParams)
|
|
{
|
|
t_FmPcdFrmReplicGroup *p_ReplicGroup = (t_FmPcdFrmReplicGroup*) h_ReplicGroup;
|
|
t_FmPcdFrmReplicMember *p_NewMember, *p_CurrentMember = NULL, *p_PreviousMember = NULL;
|
|
t_Error err;
|
|
uint8_t memberPosition;
|
|
|
|
SANITY_CHECK_RETURN_ERROR(p_ReplicGroup, E_INVALID_HANDLE);
|
|
SANITY_CHECK_RETURN_ERROR(p_MemberParams, E_INVALID_HANDLE);
|
|
|
|
/* group lock */
|
|
err = FrmReplicGroupTryLock(p_ReplicGroup);
|
|
if (GET_ERROR_TYPE(err) == E_BUSY)
|
|
return ERROR_CODE(E_BUSY);
|
|
|
|
if (memberIndex > p_ReplicGroup->numOfEntries)
|
|
{
|
|
/* unlock */
|
|
FrmReplicGroupUnlock(p_ReplicGroup);
|
|
RETURN_ERROR(MAJOR, E_INVALID_SELECTION,
|
|
("memberIndex is greater than the members in the list"));
|
|
}
|
|
|
|
if (memberIndex >= p_ReplicGroup->maxNumOfEntries)
|
|
{
|
|
/* unlock */
|
|
FrmReplicGroupUnlock(p_ReplicGroup);
|
|
RETURN_ERROR(MAJOR, E_INVALID_SELECTION, ("memberIndex is greater than the allowed number of members in the group"));
|
|
}
|
|
|
|
if ((p_ReplicGroup->numOfEntries + 1) > FM_PCD_FRM_REPLIC_MAX_NUM_OF_ENTRIES)
|
|
{
|
|
/* unlock */
|
|
FrmReplicGroupUnlock(p_ReplicGroup);
|
|
RETURN_ERROR(MAJOR, E_INVALID_VALUE,
|
|
("numOfEntries with new entry can not be larger than %d\n",
|
|
FM_PCD_FRM_REPLIC_MAX_NUM_OF_ENTRIES));
|
|
}
|
|
|
|
err = MemberCheckParams(p_ReplicGroup->h_FmPcd, p_MemberParams);
|
|
if (err)
|
|
{
|
|
/* unlock */
|
|
FrmReplicGroupUnlock(p_ReplicGroup);
|
|
RETURN_ERROR(MAJOR, err, ("member check parameters in add operation"));
|
|
}
|
|
/* determine the member position in the group */
|
|
memberPosition = GetMemberPosition(p_ReplicGroup,
|
|
memberIndex,
|
|
TRUE/* add operation */);
|
|
|
|
/* Initialize a new member */
|
|
p_NewMember = InitMember(p_ReplicGroup,
|
|
p_MemberParams,
|
|
(memberPosition == FRM_REPLIC_LAST_MEMBER_INDEX ? TRUE : FALSE));
|
|
if (!p_NewMember)
|
|
{
|
|
/* unlock */
|
|
FrmReplicGroupUnlock(p_ReplicGroup);
|
|
RETURN_ERROR(MAJOR, E_INVALID_HANDLE, ("No available member"));
|
|
}
|
|
|
|
switch (memberPosition)
|
|
{
|
|
case FRM_REPLIC_FIRST_MEMBER_INDEX:
|
|
p_CurrentMember = GetMemberByIndex(p_ReplicGroup, memberIndex);
|
|
ASSERT_COND(p_CurrentMember);
|
|
|
|
LinkMemberToMember(p_ReplicGroup, p_NewMember, p_CurrentMember);
|
|
|
|
/* update the internal group source TD */
|
|
LinkSourceToMember(p_ReplicGroup,
|
|
p_ReplicGroup->p_SourceTd,
|
|
p_NewMember);
|
|
|
|
/* add member to the internal sw member list */
|
|
AddMemberToList(p_ReplicGroup,
|
|
p_NewMember,
|
|
&p_ReplicGroup->membersList);
|
|
break;
|
|
|
|
case FRM_REPLIC_MIDDLE_MEMBER_INDEX:
|
|
p_CurrentMember = GetMemberByIndex(p_ReplicGroup, memberIndex);
|
|
ASSERT_COND(p_CurrentMember);
|
|
|
|
p_PreviousMember = GetMemberByIndex(p_ReplicGroup, (uint16_t)(memberIndex-1));
|
|
ASSERT_COND(p_PreviousMember);
|
|
|
|
LinkMemberToMember(p_ReplicGroup, p_NewMember, p_CurrentMember);
|
|
LinkMemberToMember(p_ReplicGroup, p_PreviousMember, p_NewMember);
|
|
|
|
AddMemberToList(p_ReplicGroup, p_NewMember, &p_PreviousMember->node);
|
|
break;
|
|
|
|
case FRM_REPLIC_LAST_MEMBER_INDEX:
|
|
p_PreviousMember = GetMemberByIndex(p_ReplicGroup, (uint16_t)(memberIndex-1));
|
|
ASSERT_COND(p_PreviousMember);
|
|
|
|
LinkMemberToMember(p_ReplicGroup, p_PreviousMember, p_NewMember);
|
|
FillReplicAdOfTypeResult(p_PreviousMember->p_MemberAd, FALSE/*last*/);
|
|
|
|
/* add the new member to the internal sw member list */
|
|
AddMemberToList(p_ReplicGroup, p_NewMember, &p_PreviousMember->node);
|
|
break;
|
|
|
|
default:
|
|
/* unlock */
|
|
FrmReplicGroupUnlock(p_ReplicGroup);
|
|
RETURN_ERROR(MAJOR, E_INVALID_SELECTION, ("member position in add member"));
|
|
|
|
}
|
|
|
|
/* unlock */
|
|
FrmReplicGroupUnlock(p_ReplicGroup);
|
|
|
|
return E_OK;
|
|
}
|
|
|
|
t_Error FM_PCD_FrmReplicRemoveMember(t_Handle h_ReplicGroup,
|
|
uint16_t memberIndex)
|
|
{
|
|
t_FmPcdFrmReplicGroup *p_ReplicGroup = (t_FmPcdFrmReplicGroup*) h_ReplicGroup;
|
|
t_Error err;
|
|
|
|
SANITY_CHECK_RETURN_ERROR(p_ReplicGroup, E_INVALID_HANDLE);
|
|
|
|
/* lock */
|
|
err = FrmReplicGroupTryLock(p_ReplicGroup);
|
|
if (GET_ERROR_TYPE(err) == E_BUSY)
|
|
return ERROR_CODE(E_BUSY);
|
|
|
|
if (memberIndex >= p_ReplicGroup->numOfEntries)
|
|
RETURN_ERROR(MAJOR, E_INVALID_SELECTION, ("member index to remove"));
|
|
|
|
/* Design decision: group must contain at least one member
|
|
No possibility to remove the last member from the group */
|
|
if (p_ReplicGroup->numOfEntries == 1)
|
|
RETURN_ERROR(MAJOR, E_CONFLICT, ("Can't remove the last member. At least one member should be related to a group."));
|
|
|
|
err = RemoveMember(p_ReplicGroup, memberIndex);
|
|
|
|
/* unlock */
|
|
FrmReplicGroupUnlock(p_ReplicGroup);
|
|
|
|
switch (GET_ERROR_TYPE(err))
|
|
{
|
|
case E_OK:
|
|
return E_OK;
|
|
|
|
case E_BUSY:
|
|
DBG(TRACE, ("E_BUSY error"));
|
|
return ERROR_CODE(E_BUSY);
|
|
|
|
default:
|
|
RETURN_ERROR(MAJOR, err, NO_MSG);
|
|
}
|
|
}
|
|
|
|
/*********************** End of API routines ************************/
|
|
|
|
|