| /* |
| * Copyright (c) 2011-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. |
| */ |
| |
| /* |
| * This file was originally distributed by Qualcomm Atheros, Inc. |
| * under proprietary terms before Copyright ownership was assigned |
| * to the Linux Foundation. |
| */ |
| |
| /** ------------------------------------------------------------------------- * |
| ------------------------------------------------------------------------- * |
| \file csrLinkList.c |
| |
| Implementation for the Common link list interfaces. |
| ========================================================================== */ |
| |
| #include "palApi.h" |
| #include "csrLinkList.h" |
| #include "vos_lock.h" |
| #include "vos_memory.h" |
| #include "vos_trace.h" |
| #include "vos_timer.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; |
| pList->cmdTimeoutTimer = NULL; |
| 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); |
| } |
| } else { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: pList->Flag = %d", __func__, pList->Flag); |
| } |
| } |
| |
| |
| |
| 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); |
| } |
| if ( pList->cmdTimeoutTimer && pList->cmdTimeoutDuration ) |
| { |
| /* timer to detect pending command in activelist*/ |
| vos_timer_start( pList->cmdTimeoutTimer, |
| pList->cmdTimeoutDuration); |
| } |
| } else { |
| VOS_TRACE(VOS_MODULE_ID_SME, VOS_TRACE_LEVEL_INFO_HIGH, |
| "%s: pList->Flag = %d", __func__, pList->Flag); |
| } |
| } |
| |
| |
| 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 ); |
| } |
| if ( pList->cmdTimeoutTimer ) |
| { |
| vos_timer_stop(pList->cmdTimeoutTimer); |
| } |
| } |
| |
| 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 ); |
| } |