1  /*
2   * Copyright (c) 2013-2014, 2017, 2019 The Linux Foundation. All rights reserved.
3   * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
4   *
5   * Permission to use, copy, modify, and/or distribute this software for
6   * any purpose with or without fee is hereby granted, provided that the
7   * above copyright notice and this permission notice appear in all
8   * copies.
9   *
10   * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
11   * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
12   * WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
13   * AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
14   * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
15   * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
16   * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
17   * PERFORMANCE OF THIS SOFTWARE.
18   */
19  
20  /*=========================================================================== */
21  /* Double-link list definitions (adapted from Atheros SDIO stack) */
22  /* */
23  /* Author(s): ="Atheros" */
24  /*=========================================================================== */
25  
26  #ifndef __DL_LIST_H___
27  #define __DL_LIST_H___
28  
29  #include "qdf_util.h"
30  
31  #define A_CONTAINING_STRUCT(address, struct_type, field_name) \
32  	(qdf_container_of(address, struct_type, field_name))
33  
34  /* list functions */
35  /* pointers for the list */
36  typedef struct _DL_LIST {
37  	struct _DL_LIST *pPrev;
38  	struct _DL_LIST *pNext;
39  } DL_LIST, *PDL_LIST;
40  /*
41   * DL_LIST_INIT , initialize doubly linked list
42   */
43  #define DL_LIST_INIT(pList) \
44  	{(pList)->pPrev = pList; (pList)->pNext = pList; }
45  
46  /* faster macro to init list and add a single item */
47  #define DL_LIST_INIT_AND_ADD(pList, pItem) \
48  	{   (pList)->pPrev = (pItem); \
49  	    (pList)->pNext = (pItem); \
50  	    (pItem)->pNext = (pList); \
51  	    (pItem)->pPrev = (pList); \
52  	}
53  
54  #define DL_LIST_IS_EMPTY(pList) (((pList)->pPrev == (pList)) && \
55  				((pList)->pNext == (pList)))
56  #define DL_LIST_GET_ITEM_AT_HEAD(pList) (pList)->pNext
57  #define DL_LIST_GET_ITEM_AT_TAIL(pList) (pList)->pPrev
58  /*
59   * ITERATE_OVER_LIST pStart is the list, pTemp is a temp list member
60   * NOT: do not use this function if the items in the list are deleted inside the
61   * iteration loop
62   */
63  #define ITERATE_OVER_LIST(pStart, pTemp) \
64  		for ((pTemp) = (pStart)->pNext; pTemp != (pStart); \
65  					(pTemp) = (pTemp)->pNext)
66  
dl_list_is_entry_in_list(const DL_LIST * pList,const DL_LIST * pEntry)67  static inline bool dl_list_is_entry_in_list(const DL_LIST *pList,
68  					      const DL_LIST *pEntry)
69  {
70  	const DL_LIST *pTmp;
71  
72  	if (pList == pEntry)
73  		return true;
74  
75  	ITERATE_OVER_LIST(pList, pTmp) {
76  		if (pTmp == pEntry)
77  			return true;
78  	}
79  
80  	return false;
81  }
82  
83  /* safe iterate macro that allows the item to be removed from the list
84   * the iteration continues to the next item in the list
85   */
86  #define ITERATE_OVER_LIST_ALLOW_REMOVE(pStart, pItem, st, offset) \
87  	{							\
88  		PDL_LIST pTemp;					    \
89  		{ pTemp = (pStart)->pNext; }			    \
90  		while (pTemp != (pStart)) {			    \
91  			{ (pItem) = A_CONTAINING_STRUCT(pTemp, st, offset); } \
92  			{ pTemp = pTemp->pNext; }			       \
93  
94  #define ITERATE_IS_VALID(pStart)    dl_list_is_entry_in_list(pStart, pTemp)
95  #define ITERATE_RESET(pStart) { pTemp = (pStart)->pNext; }
96  
97  #define ITERATE_END }}
98  
99  /*
100   * dl_list_insert_tail - insert pAdd to the end of the list
101   */
dl_list_insert_tail(PDL_LIST pList,PDL_LIST pAdd)102  static inline PDL_LIST dl_list_insert_tail(PDL_LIST pList, PDL_LIST pAdd)
103  {
104  	/* insert at tail */
105  	pAdd->pPrev = pList->pPrev;
106  	pAdd->pNext = pList;
107  	if (pList->pPrev)
108  		pList->pPrev->pNext = pAdd;
109  	pList->pPrev = pAdd;
110  	return pAdd;
111  }
112  
113  /*
114   * dl_list_insert_head - insert pAdd into the head of the list
115   */
dl_list_insert_head(PDL_LIST pList,PDL_LIST pAdd)116  static inline PDL_LIST dl_list_insert_head(PDL_LIST pList, PDL_LIST pAdd)
117  {
118  	/* insert at head */
119  	pAdd->pPrev = pList;
120  	pAdd->pNext = pList->pNext;
121  	pList->pNext->pPrev = pAdd;
122  	pList->pNext = pAdd;
123  	return pAdd;
124  }
125  
126  #define DL_ListAdd(pList, pItem) dl_list_insert_head((pList), (pItem))
127  /*
128   * dl_list_remove - remove pDel from list
129   */
dl_list_remove(PDL_LIST pDel)130  static inline PDL_LIST dl_list_remove(PDL_LIST pDel)
131  {
132  	if (pDel->pNext)
133  		pDel->pNext->pPrev = pDel->pPrev;
134  	if (pDel->pPrev)
135  		pDel->pPrev->pNext = pDel->pNext;
136  	/* point back to itself just to be safe, if remove is called again */
137  	pDel->pNext = pDel;
138  	pDel->pPrev = pDel;
139  	return pDel;
140  }
141  
142  /*
143   * dl_list_remove_item_from_head - get a list item from the head
144   */
dl_list_remove_item_from_head(PDL_LIST pList)145  static inline PDL_LIST dl_list_remove_item_from_head(PDL_LIST pList)
146  {
147  	PDL_LIST pItem = NULL;
148  
149  	if (pList->pNext != pList) {
150  		pItem = pList->pNext;
151  		/* remove the first item from head */
152  		dl_list_remove(pItem);
153  	}
154  	return pItem;
155  }
156  
dl_list_remove_item_from_tail(PDL_LIST pList)157  static inline PDL_LIST dl_list_remove_item_from_tail(PDL_LIST pList)
158  {
159  	PDL_LIST pItem = NULL;
160  
161  	if (pList->pPrev != pList) {
162  		pItem = pList->pPrev;
163  		/* remove the item from tail */
164  		dl_list_remove(pItem);
165  	}
166  	return pItem;
167  }
168  
169  /* transfer src list items to the tail of the destination list */
dl_list_transfer_items_to_tail(PDL_LIST pDest,PDL_LIST pSrc)170  static inline void dl_list_transfer_items_to_tail(PDL_LIST pDest, PDL_LIST pSrc)
171  {
172  	/* only concatenate if src is not empty */
173  	if (!DL_LIST_IS_EMPTY(pSrc)) {
174  		/* cut out circular list in src and re-attach to end of dest */
175  		pSrc->pPrev->pNext = pDest;
176  		pSrc->pNext->pPrev = pDest->pPrev;
177  		pDest->pPrev->pNext = pSrc->pNext;
178  		pDest->pPrev = pSrc->pPrev;
179  		/* terminate src list, it is now empty */
180  		pSrc->pPrev = pSrc;
181  		pSrc->pNext = pSrc;
182  	}
183  }
184  
185  /* transfer src list items to the head of the destination list */
dl_list_transfer_items_to_head(PDL_LIST pDest,PDL_LIST pSrc)186  static inline void dl_list_transfer_items_to_head(PDL_LIST pDest, PDL_LIST pSrc)
187  {
188  	/* only concatenate if src is not empty */
189  	if (!DL_LIST_IS_EMPTY(pSrc)) {
190  		/* cut out circular list in src and reattach to start of dest */
191  		pSrc->pNext->pPrev = pDest;
192  		pDest->pNext->pPrev = pSrc->pPrev;
193  		pSrc->pPrev->pNext = pDest->pNext;
194  		pDest->pNext = pSrc->pNext;
195  		/* terminate src list, it is now empty */
196  		pSrc->pPrev = pSrc;
197  		pSrc->pNext = pSrc;
198  	}
199  }
200  
201  #endif /* __DL_LIST_H___ */
202