blob: 998321299a0e370e1a6ebc356c6749a53ded62c9 [file] [log] [blame]
/*
* Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
*
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all
* copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
/*
* Copyright (c) 2012, The Linux Foundation. All rights reserved.
*
* Previously licensed under the ISC license by Qualcomm Atheros, Inc.
*
*
* Permission to use, copy, modify, and/or distribute this software for
* any purpose with or without fee is hereby granted, provided that the
* above copyright notice and this permission notice appear in all
* copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
* AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
* TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/
/** ------------------------------------------------------------------------- *
------------------------------------------------------------------------- *
\file csrLinkList.c
Implementation for the Common link list interfaces.
Copyright (C) 2006 Airgo Networks, Incorporated
========================================================================== */
#include "palApi.h"
#include "csrLinkList.h"
#include "vos_lock.h"
#include "vos_memory.h"
#include "vos_trace.h"
ANI_INLINE_FUNCTION void csrListInit(tListElem *pList)
{
pList->last = pList->next = pList;
}
ANI_INLINE_FUNCTION void csrListRemoveEntry(tListElem *pEntry)
{
tListElem *pLast;
tListElem *pNext;
pLast = pEntry->last;
pNext = pEntry->next;
pLast->next = pNext;
pNext->last = pLast;
}
ANI_INLINE_FUNCTION tListElem * csrListRemoveHead(tListElem *pHead)
{
tListElem *pEntry;
tListElem *pNext;
pEntry = pHead->next;
pNext = pEntry->next;
pHead->next = pNext;
pNext->last = pHead;
return (pEntry);
}
ANI_INLINE_FUNCTION tListElem * csrListRemoveTail(tListElem *pHead)
{
tListElem *pEntry;
tListElem *pLast;
pEntry = pHead->last;
pLast = pEntry->last;
pHead->last = pLast;
pLast->next = pHead;
return (pEntry);
}
ANI_INLINE_FUNCTION void csrListInsertTail(tListElem *pHead, tListElem *pEntry)
{
tListElem *pLast;
pLast = pHead->last;
pEntry->last = pLast;
pEntry->next = pHead;
pLast->next = pEntry;
pHead->last = pEntry;
}
ANI_INLINE_FUNCTION void csrListInsertHead(tListElem *pHead, tListElem *pEntry)
{
tListElem *pNext;
pNext = pHead->next;
pEntry->next = pNext;
pEntry->last = pHead;
pNext->last = pEntry;
pHead->next = pEntry;
}
//Insert pNewEntry before pEntry
void csrListInsertEntry(tListElem *pEntry, tListElem *pNewEntry)
{
tListElem *pLast;
if( !pEntry)
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_FATAL,"%s: Error!! pEntry is Null", __func__);
return;
}
pLast = pEntry->last;
pLast->next = pNewEntry;
pEntry->last = pNewEntry;
pNewEntry->next = pEntry;
pNewEntry->last = pLast;
}
tANI_U32 csrLLCount( tDblLinkList *pList )
{
tANI_U32 c = 0;
if( !pList)
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_FATAL,"%s: Error!! pList is Null", __func__);
return c;
}
if ( pList && ( LIST_FLAG_OPEN == pList->Flag ) )
{
c = pList->Count;
}
return( c );
}
void csrLLLock( tDblLinkList *pList )
{
if( !pList)
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_FATAL,"%s: Error!! pList is Null", __func__);
return ;
}
if ( LIST_FLAG_OPEN == pList->Flag )
{
vos_lock_acquire(&pList->Lock);
}
}
void csrLLUnlock( tDblLinkList *pList )
{
if( !pList)
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_FATAL,"%s: Error!! pList is Null", __func__);
return ;
}
if ( LIST_FLAG_OPEN == pList->Flag )
{
vos_lock_release(&pList->Lock);
}
}
tANI_BOOLEAN csrLLIsListEmpty( tDblLinkList *pList, tANI_BOOLEAN fInterlocked )
{
tANI_BOOLEAN fEmpty = eANI_BOOLEAN_TRUE;
if( !pList)
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_FATAL,"%s: Error!! pList is Null", __func__);
return fEmpty ;
}
if ( LIST_FLAG_OPEN == pList->Flag )
{
if(fInterlocked)
{
csrLLLock(pList);
}
fEmpty = csrIsListEmpty( &pList->ListHead );
if(fInterlocked)
{
csrLLUnlock(pList);
}
}
return( fEmpty );
}
tANI_BOOLEAN csrLLFindEntry( tDblLinkList *pList, tListElem *pEntryToFind )
{
tANI_BOOLEAN fFound = eANI_BOOLEAN_FALSE;
tListElem *pEntry;
if( !pList)
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_FATAL,"%s: Error!! pList is Null", __func__);
return fFound ;
}
if ( LIST_FLAG_OPEN == pList->Flag )
{
pEntry = csrLLPeekHead( pList, LL_ACCESS_NOLOCK);
// Have to make sure we don't loop back to the head of the list, which will
// happen if the entry is NOT on the list...
while( pEntry && ( pEntry != &pList->ListHead ) )
{
if ( pEntry == pEntryToFind )
{
fFound = eANI_BOOLEAN_TRUE;
break;
}
pEntry = pEntry->next;
}
}
return( fFound );
}
eHalStatus csrLLOpen( tHddHandle hHdd, tDblLinkList *pList )
{
eHalStatus status = eHAL_STATUS_SUCCESS;
VOS_STATUS vosStatus;
if( !pList)
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_FATAL,"%s: Error!! pList is Null", __func__);
return eHAL_STATUS_FAILURE ;
}
if ( LIST_FLAG_OPEN != pList->Flag )
{
pList->Count = 0;
vosStatus = vos_lock_init(&pList->Lock);
if(VOS_IS_STATUS_SUCCESS(vosStatus))
{
csrListInit( &pList->ListHead );
pList->Flag = LIST_FLAG_OPEN;
pList->hHdd = hHdd;
}
else
{
status = eHAL_STATUS_FAILURE;
}
}
return (status);
}
void csrLLClose( tDblLinkList *pList )
{
if( !pList)
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_FATAL,"%s: Error!! pList is Null", __func__);
return ;
}
if ( LIST_FLAG_OPEN == pList->Flag )
{
// Make sure the list is empty...
csrLLPurge( pList, LL_ACCESS_LOCK );
vos_lock_destroy( &pList->Lock );
pList->Flag = LIST_FLAG_CLOSE;
}
}
void csrLLInsertTail( tDblLinkList *pList, tListElem *pEntry, tANI_BOOLEAN fInterlocked )
{
if( !pList)
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_FATAL,"%s: Error!! pList is Null", __func__);
return;
}
if ( LIST_FLAG_OPEN == pList->Flag )
{
if(fInterlocked)
{
csrLLLock(pList);
}
csrListInsertTail( &pList->ListHead, pEntry );
pList->Count++;
if(fInterlocked)
{
csrLLUnlock(pList);
}
}
}
void csrLLInsertHead( tDblLinkList *pList, tListElem *pEntry, tANI_BOOLEAN fInterlocked )
{
if( !pList)
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_FATAL,"%s: Error!! pList is Null", __func__);
return;
}
if ( LIST_FLAG_OPEN == pList->Flag )
{
if(fInterlocked)
{
csrLLLock(pList);
}
csrListInsertHead( &pList->ListHead, pEntry );
pList->Count++;
if(fInterlocked)
{
csrLLUnlock(pList);
}
}
}
void csrLLInsertEntry( tDblLinkList *pList, tListElem *pEntry, tListElem *pNewEntry, tANI_BOOLEAN fInterlocked )
{
if( !pList)
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_FATAL,"%s: Error!! pList is Null", __func__);
return ;
}
if ( LIST_FLAG_OPEN == pList->Flag )
{
if(fInterlocked)
{
csrLLLock(pList);
}
csrListInsertEntry( pEntry, pNewEntry );
pList->Count++;
if(fInterlocked)
{
csrLLUnlock(pList);
}
}
}
tListElem *csrLLRemoveTail( tDblLinkList *pList, tANI_BOOLEAN fInterlocked )
{
tListElem *pEntry = NULL;
if( !pList)
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_FATAL,"%s: Error!! pList is Null", __func__);
return pEntry ;
}
if ( LIST_FLAG_OPEN == pList->Flag )
{
if ( fInterlocked )
{
csrLLLock( pList );
}
if ( !csrIsListEmpty(&pList->ListHead) )
{
pEntry = csrListRemoveTail( &pList->ListHead );
pList->Count--;
}
if ( fInterlocked )
{
csrLLUnlock( pList );
}
}
return( pEntry );
}
tListElem *csrLLPeekTail( tDblLinkList *pList, tANI_BOOLEAN fInterlocked )
{
tListElem *pEntry = NULL;
if( !pList)
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_FATAL,"%s: Error!! pList is Null", __func__);
return pEntry ;
}
if ( LIST_FLAG_OPEN == pList->Flag )
{
if ( fInterlocked )
{
csrLLLock( pList );
}
if ( !csrIsListEmpty(&pList->ListHead) )
{
pEntry = pList->ListHead.last;
}
if ( fInterlocked )
{
csrLLUnlock( pList );
}
}
return( pEntry );
}
tListElem *csrLLRemoveHead( tDblLinkList *pList, tANI_BOOLEAN fInterlocked )
{
tListElem *pEntry = NULL;
if( !pList)
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_FATAL,"%s: Error!! pList is Null", __func__);
return pEntry ;
}
if ( LIST_FLAG_OPEN == pList->Flag )
{
if ( fInterlocked )
{
csrLLLock( pList );
}
if ( !csrIsListEmpty(&pList->ListHead) )
{
pEntry = csrListRemoveHead( &pList->ListHead );
pList->Count--;
}
if ( fInterlocked )
{
csrLLUnlock( pList );
}
}
return( pEntry );
}
tListElem *csrLLPeekHead( tDblLinkList *pList, tANI_BOOLEAN fInterlocked )
{
tListElem *pEntry = NULL;
if( !pList)
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_FATAL,"%s: Error!! pList is Null", __func__);
return pEntry ;
}
if ( LIST_FLAG_OPEN == pList->Flag )
{
if ( fInterlocked )
{
csrLLLock( pList );
}
if ( !csrIsListEmpty(&pList->ListHead) )
{
pEntry = pList->ListHead.next;
}
if ( fInterlocked )
{
csrLLUnlock( pList );
}
}
return( pEntry );
}
void csrLLPurge( tDblLinkList *pList, tANI_BOOLEAN fInterlocked )
{
tListElem *pEntry;
if( !pList)
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_FATAL,"%s: Error!! pList is Null", __func__);
return ;
}
if ( LIST_FLAG_OPEN == pList->Flag )
{
if ( fInterlocked )
{
csrLLLock( pList );
}
while( (pEntry = csrLLRemoveHead( pList, LL_ACCESS_NOLOCK )) )
{
// just remove everything from the list until
// nothing left on the list.
}
if ( fInterlocked )
{
csrLLUnlock( pList );
}
}
}
tANI_BOOLEAN csrLLRemoveEntry( tDblLinkList *pList, tListElem *pEntryToRemove, tANI_BOOLEAN fInterlocked )
{
tANI_BOOLEAN fFound = eANI_BOOLEAN_FALSE;
tListElem *pEntry;
if( !pList)
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_FATAL,"%s: Error!! pList is Null", __func__);
return fFound;
}
if ( LIST_FLAG_OPEN == pList->Flag )
{
if ( fInterlocked )
{
csrLLLock( pList );
}
pEntry = csrLLPeekHead( pList, LL_ACCESS_NOLOCK );
// Have to make sure we don't loop back to the head of the list, which will
// happen if the entry is NOT on the list...
while( pEntry && ( pEntry != &pList->ListHead ) )
{
if ( pEntry == pEntryToRemove )
{
csrListRemoveEntry( pEntry );
pList->Count--;
fFound = eANI_BOOLEAN_TRUE;
break;
}
pEntry = pEntry->next;
}
if ( fInterlocked )
{
csrLLUnlock( pList );
}
}
return( fFound );
}
tListElem *csrLLNext( tDblLinkList *pList, tListElem *pEntry, tANI_BOOLEAN fInterlocked )
{
tListElem *pNextEntry = NULL;
if( !pList)
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_FATAL,"%s: Error!! pList is Null", __func__);
return pNextEntry ;
}
if ( LIST_FLAG_OPEN == pList->Flag )
{
if ( fInterlocked )
{
csrLLLock( pList );
}
if ( !csrIsListEmpty(&pList->ListHead) && csrLLFindEntry( pList, pEntry ) )
{
pNextEntry = pEntry->next;
//Make sure we don't walk past the head
if ( pNextEntry == &pList->ListHead )
{
pNextEntry = NULL;
}
}
if ( fInterlocked )
{
csrLLUnlock( pList );
}
}
return( pNextEntry );
}
tListElem *csrLLPrevious( tDblLinkList *pList, tListElem *pEntry, tANI_BOOLEAN fInterlocked )
{
tListElem *pNextEntry = NULL;
if( !pList)
{
VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_FATAL,"%s: Error!! pList is Null", __func__);
return pNextEntry ;
}
if ( LIST_FLAG_OPEN == pList->Flag )
{
if ( fInterlocked )
{
csrLLLock( pList );
}
if ( !csrIsListEmpty(&pList->ListHead) && csrLLFindEntry( pList, pEntry ) )
{
pNextEntry = pEntry->last;
//Make sure we don't walk past the head
if ( pNextEntry == &pList->ListHead )
{
pNextEntry = NULL;
}
}
if ( fInterlocked )
{
csrLLUnlock( pList );
}
}
return( pNextEntry );
}