blob: 1fa0b8b6f5a7ef9c551a4870a18746b2ccb35a87 [file] [log] [blame]
/*
* Copyright (c) 2011-2012, 2014-2015 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 csr_link_list.c
Implementation for the Common link list interfaces.
========================================================================== */
#include "csr_link_list.h"
#include "cdf_lock.h"
#include "cdf_memory.h"
#include "cdf_trace.h"
#include "cdf_mc_timer.h"
CDF_INLINE_FN void csr_list_init(tListElem *pList)
{
pList->last = pList->next = pList;
}
CDF_INLINE_FN void csr_list_remove_entry(tListElem *pEntry)
{
tListElem *pLast;
tListElem *pNext;
pLast = pEntry->last;
pNext = pEntry->next;
pLast->next = pNext;
pNext->last = pLast;
}
CDF_INLINE_FN tListElem *csr_list_remove_head(tListElem *pHead)
{
tListElem *pEntry;
tListElem *pNext;
pEntry = pHead->next;
pNext = pEntry->next;
pHead->next = pNext;
pNext->last = pHead;
return pEntry;
}
CDF_INLINE_FN tListElem *csr_list_remove_tail(tListElem *pHead)
{
tListElem *pEntry;
tListElem *pLast;
pEntry = pHead->last;
pLast = pEntry->last;
pHead->last = pLast;
pLast->next = pHead;
return pEntry;
}
CDF_INLINE_FN void csr_list_insert_tail(tListElem *pHead, tListElem *pEntry)
{
tListElem *pLast;
pLast = pHead->last;
pEntry->last = pLast;
pEntry->next = pHead;
pLast->next = pEntry;
pHead->last = pEntry;
}
CDF_INLINE_FN void csr_list_insert_head(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 csr_list_insert_entry(tListElem *pEntry, tListElem *pNewEntry)
{
tListElem *pLast;
if (!pEntry) {
CDF_TRACE(CDF_MODULE_ID_SME, CDF_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;
}
uint32_t csr_ll_count(tDblLinkList *pList)
{
uint32_t c = 0;
if (!pList) {
CDF_TRACE(CDF_MODULE_ID_SME, CDF_TRACE_LEVEL_FATAL,
"%s: Error!! pList is Null", __func__);
return c;
}
if (pList && (LIST_FLAG_OPEN == pList->Flag)) {
c = pList->Count;
}
return c;
}
void csr_ll_lock(tDblLinkList *pList)
{
if (!pList) {
CDF_TRACE(CDF_MODULE_ID_SME, CDF_TRACE_LEVEL_FATAL,
"%s: Error!! pList is Null", __func__);
return;
}
if (LIST_FLAG_OPEN == pList->Flag) {
cdf_mutex_acquire(&pList->Lock);
}
}
void csr_ll_unlock(tDblLinkList *pList)
{
if (!pList) {
CDF_TRACE(CDF_MODULE_ID_SME, CDF_TRACE_LEVEL_FATAL,
"%s: Error!! pList is Null", __func__);
return;
}
if (LIST_FLAG_OPEN == pList->Flag) {
cdf_mutex_release(&pList->Lock);
}
}
bool csr_ll_is_list_empty(tDblLinkList *pList, bool fInterlocked)
{
bool fEmpty = true;
if (!pList) {
CDF_TRACE(CDF_MODULE_ID_SME, CDF_TRACE_LEVEL_FATAL,
"%s: Error!! pList is Null", __func__);
return fEmpty;
}
if (LIST_FLAG_OPEN == pList->Flag) {
if (fInterlocked) {
csr_ll_lock(pList);
}
fEmpty = csrIsListEmpty(&pList->ListHead);
if (fInterlocked) {
csr_ll_unlock(pList);
}
}
return fEmpty;
}
bool csr_ll_find_entry(tDblLinkList *pList, tListElem *pEntryToFind)
{
bool fFound = false;
tListElem *pEntry;
if (!pList) {
CDF_TRACE(CDF_MODULE_ID_SME, CDF_TRACE_LEVEL_FATAL,
"%s: Error!! pList is Null", __func__);
return fFound;
}
if (LIST_FLAG_OPEN == pList->Flag) {
pEntry = csr_ll_peek_head(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 = true;
break;
}
pEntry = pEntry->next;
}
}
return fFound;
}
CDF_STATUS csr_ll_open(tHddHandle hHdd, tDblLinkList *pList)
{
CDF_STATUS status = CDF_STATUS_SUCCESS;
CDF_STATUS cdf_status;
if (!pList) {
CDF_TRACE(CDF_MODULE_ID_SME, CDF_TRACE_LEVEL_FATAL,
"%s: Error!! pList is Null", __func__);
return CDF_STATUS_E_FAILURE;
}
if (LIST_FLAG_OPEN != pList->Flag) {
pList->Count = 0;
pList->cmdTimeoutTimer = NULL;
cdf_status = cdf_mutex_init(&pList->Lock);
if (CDF_IS_STATUS_SUCCESS(cdf_status)) {
csr_list_init(&pList->ListHead);
pList->Flag = LIST_FLAG_OPEN;
pList->hHdd = hHdd;
} else {
status = CDF_STATUS_E_FAILURE;
}
}
return status;
}
void csr_ll_close(tDblLinkList *pList)
{
if (!pList) {
CDF_TRACE(CDF_MODULE_ID_SME, CDF_TRACE_LEVEL_FATAL,
"%s: Error!! pList is Null", __func__);
return;
}
if (LIST_FLAG_OPEN == pList->Flag) {
/* Make sure the list is empty... */
csr_ll_purge(pList, LL_ACCESS_LOCK);
cdf_mutex_destroy(&pList->Lock);
pList->Flag = LIST_FLAG_CLOSE;
}
}
void csr_ll_insert_tail(tDblLinkList *pList, tListElem *pEntry,
bool fInterlocked)
{
if (!pList) {
CDF_TRACE(CDF_MODULE_ID_SME, CDF_TRACE_LEVEL_FATAL,
"%s: Error!! pList is Null", __func__);
return;
}
if (LIST_FLAG_OPEN == pList->Flag) {
if (fInterlocked) {
csr_ll_lock(pList);
}
csr_list_insert_tail(&pList->ListHead, pEntry);
pList->Count++;
if (fInterlocked) {
csr_ll_unlock(pList);
}
}
}
void csr_ll_insert_head(tDblLinkList *pList, tListElem *pEntry,
bool fInterlocked)
{
if (!pList) {
CDF_TRACE(CDF_MODULE_ID_SME, CDF_TRACE_LEVEL_FATAL,
"%s: Error!! pList is Null", __func__);
return;
}
if (LIST_FLAG_OPEN == pList->Flag) {
if (fInterlocked) {
csr_ll_lock(pList);
}
csr_list_insert_head(&pList->ListHead, pEntry);
pList->Count++;
if (fInterlocked) {
csr_ll_unlock(pList);
}
if (pList->cmdTimeoutTimer && pList->cmdTimeoutDuration) {
/* timer to detect pending command in activelist */
cdf_mc_timer_start(pList->cmdTimeoutTimer,
pList->cmdTimeoutDuration);
}
}
}
void csr_ll_insert_entry(tDblLinkList *pList, tListElem *pEntry,
tListElem *pNewEntry, bool fInterlocked)
{
if (!pList) {
CDF_TRACE(CDF_MODULE_ID_SME, CDF_TRACE_LEVEL_FATAL,
"%s: Error!! pList is Null", __func__);
return;
}
if (LIST_FLAG_OPEN == pList->Flag) {
if (fInterlocked) {
csr_ll_lock(pList);
}
csr_list_insert_entry(pEntry, pNewEntry);
pList->Count++;
if (fInterlocked) {
csr_ll_unlock(pList);
}
}
}
tListElem *csr_ll_remove_tail(tDblLinkList *pList, bool fInterlocked)
{
tListElem *pEntry = NULL;
if (!pList) {
CDF_TRACE(CDF_MODULE_ID_SME, CDF_TRACE_LEVEL_FATAL,
"%s: Error!! pList is Null", __func__);
return pEntry;
}
if (LIST_FLAG_OPEN == pList->Flag) {
if (fInterlocked) {
csr_ll_lock(pList);
}
if (!csrIsListEmpty(&pList->ListHead)) {
pEntry = csr_list_remove_tail(&pList->ListHead);
pList->Count--;
}
if (fInterlocked) {
csr_ll_unlock(pList);
}
}
return pEntry;
}
tListElem *csr_ll_peek_tail(tDblLinkList *pList, bool fInterlocked)
{
tListElem *pEntry = NULL;
if (!pList) {
CDF_TRACE(CDF_MODULE_ID_SME, CDF_TRACE_LEVEL_FATAL,
"%s: Error!! pList is Null", __func__);
return pEntry;
}
if (LIST_FLAG_OPEN == pList->Flag) {
if (fInterlocked) {
csr_ll_lock(pList);
}
if (!csrIsListEmpty(&pList->ListHead)) {
pEntry = pList->ListHead.last;
}
if (fInterlocked) {
csr_ll_unlock(pList);
}
}
return pEntry;
}
tListElem *csr_ll_remove_head(tDblLinkList *pList, bool fInterlocked)
{
tListElem *pEntry = NULL;
if (!pList) {
CDF_TRACE(CDF_MODULE_ID_SME, CDF_TRACE_LEVEL_FATAL,
"%s: Error!! pList is Null", __func__);
return pEntry;
}
if (LIST_FLAG_OPEN == pList->Flag) {
if (fInterlocked) {
csr_ll_lock(pList);
}
if (!csrIsListEmpty(&pList->ListHead)) {
pEntry = csr_list_remove_head(&pList->ListHead);
pList->Count--;
}
if (fInterlocked) {
csr_ll_unlock(pList);
}
}
return pEntry;
}
tListElem *csr_ll_peek_head(tDblLinkList *pList, bool fInterlocked)
{
tListElem *pEntry = NULL;
if (!pList) {
CDF_TRACE(CDF_MODULE_ID_SME, CDF_TRACE_LEVEL_FATAL,
"%s: Error!! pList is Null", __func__);
return pEntry;
}
if (LIST_FLAG_OPEN == pList->Flag) {
if (fInterlocked) {
csr_ll_lock(pList);
}
if (!csrIsListEmpty(&pList->ListHead)) {
pEntry = pList->ListHead.next;
}
if (fInterlocked) {
csr_ll_unlock(pList);
}
}
return pEntry;
}
void csr_ll_purge(tDblLinkList *pList, bool fInterlocked)
{
tListElem *pEntry;
if (!pList) {
CDF_TRACE(CDF_MODULE_ID_SME, CDF_TRACE_LEVEL_FATAL,
"%s: Error!! pList is Null", __func__);
return;
}
if (LIST_FLAG_OPEN == pList->Flag) {
if (fInterlocked) {
csr_ll_lock(pList);
}
while ((pEntry = csr_ll_remove_head(pList, LL_ACCESS_NOLOCK))) {
/* just remove everything from the list until */
/* nothing left on the list. */
}
if (fInterlocked) {
csr_ll_unlock(pList);
}
}
}
bool csr_ll_remove_entry(tDblLinkList *pList, tListElem *pEntryToRemove,
bool fInterlocked)
{
bool fFound = false;
tListElem *pEntry;
if (!pList) {
CDF_TRACE(CDF_MODULE_ID_SME, CDF_TRACE_LEVEL_FATAL,
"%s: Error!! pList is Null", __func__);
return fFound;
}
if (LIST_FLAG_OPEN == pList->Flag) {
if (fInterlocked) {
csr_ll_lock(pList);
}
pEntry = csr_ll_peek_head(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) {
csr_list_remove_entry(pEntry);
pList->Count--;
fFound = true;
break;
}
pEntry = pEntry->next;
}
if (fInterlocked) {
csr_ll_unlock(pList);
}
if (pList->cmdTimeoutTimer) {
cdf_mc_timer_stop(pList->cmdTimeoutTimer);
}
}
return fFound;
}
tListElem *csr_ll_next(tDblLinkList *pList, tListElem *pEntry,
bool fInterlocked)
{
tListElem *pNextEntry = NULL;
if (!pList) {
CDF_TRACE(CDF_MODULE_ID_SME, CDF_TRACE_LEVEL_FATAL,
"%s: Error!! pList is Null", __func__);
return pNextEntry;
}
if (LIST_FLAG_OPEN == pList->Flag) {
if (fInterlocked) {
csr_ll_lock(pList);
}
if (!csrIsListEmpty(&pList->ListHead)
&& csr_ll_find_entry(pList, pEntry)) {
pNextEntry = pEntry->next;
/* Make sure we don't walk past the head */
if (pNextEntry == &pList->ListHead) {
pNextEntry = NULL;
}
}
if (fInterlocked) {
csr_ll_unlock(pList);
}
}
return pNextEntry;
}
tListElem *csr_ll_previous(tDblLinkList *pList, tListElem *pEntry,
bool fInterlocked)
{
tListElem *pNextEntry = NULL;
if (!pList) {
CDF_TRACE(CDF_MODULE_ID_SME, CDF_TRACE_LEVEL_FATAL,
"%s: Error!! pList is Null", __func__);
return pNextEntry;
}
if (LIST_FLAG_OPEN == pList->Flag) {
if (fInterlocked) {
csr_ll_lock(pList);
}
if (!csrIsListEmpty(&pList->ListHead)
&& csr_ll_find_entry(pList, pEntry)) {
pNextEntry = pEntry->last;
/* Make sure we don't walk past the head */
if (pNextEntry == &pList->ListHead) {
pNextEntry = NULL;
}
}
if (fInterlocked) {
csr_ll_unlock(pList);
}
}
return pNextEntry;
}