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