1 /*
2  * Copyright (c) 2014-2018, 2021 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  * DOC: qdf_list.c
22  *
23  * QCA driver framework list manipulation APIs. QDF linked list
24  * APIs are NOT thread safe so make sure to use appropriate locking mechanisms
25  * to assure operations on the list are thread safe.
26  */
27 
28 /* Include files */
29 #include <qdf_list.h>
30 #include <qdf_module.h>
31 
32 /* Function declarations and documentation */
33 
qdf_list_insert_before(qdf_list_t * list,qdf_list_node_t * new_node,qdf_list_node_t * node)34 QDF_STATUS qdf_list_insert_before(qdf_list_t *list,
35 	qdf_list_node_t *new_node, qdf_list_node_t *node)
36 {
37 	list_add_tail(new_node, node);
38 	list->count++;
39 
40 	return QDF_STATUS_SUCCESS;
41 }
42 qdf_export_symbol(qdf_list_insert_before);
43 
qdf_list_insert_after(qdf_list_t * list,qdf_list_node_t * new_node,qdf_list_node_t * node)44 QDF_STATUS qdf_list_insert_after(qdf_list_t *list,
45 	qdf_list_node_t *new_node, qdf_list_node_t *node)
46 {
47 	list_add(new_node, node);
48 	list->count++;
49 
50 	return QDF_STATUS_SUCCESS;
51 }
52 qdf_export_symbol(qdf_list_insert_after);
53 
54 /**
55  * qdf_list_insert_front() - insert input node at front of the list
56  * @list: Pointer to list
57  * @node: Pointer to input node
58  *
59  * Return: QDF status
60  */
qdf_list_insert_front(qdf_list_t * list,qdf_list_node_t * node)61 QDF_STATUS qdf_list_insert_front(qdf_list_t *list, qdf_list_node_t *node)
62 {
63 	list_add(node, &list->anchor);
64 	list->count++;
65 	return QDF_STATUS_SUCCESS;
66 }
67 qdf_export_symbol(qdf_list_insert_front);
68 
69 /**
70  * qdf_list_insert_back() - insert input node at back of the list
71  * @list: Pointer to list
72  * @node: Pointer to input node
73  *
74  * Return: QDF status
75  */
qdf_list_insert_back(qdf_list_t * list,qdf_list_node_t * node)76 QDF_STATUS qdf_list_insert_back(qdf_list_t *list, qdf_list_node_t *node)
77 {
78 	list_add_tail(node, &list->anchor);
79 	list->count++;
80 	return QDF_STATUS_SUCCESS;
81 }
82 qdf_export_symbol(qdf_list_insert_back);
83 
84 /**
85  * qdf_list_insert_back_size() - insert input node at back of list and save
86  * list size
87  * @list: Pointer to list
88  * @node: Pointer to input node
89  * @p_size: Pointer to store list size
90  *
91  * Return: QDF status
92  */
qdf_list_insert_back_size(qdf_list_t * list,qdf_list_node_t * node,uint32_t * p_size)93 QDF_STATUS qdf_list_insert_back_size(qdf_list_t *list,
94 				     qdf_list_node_t *node, uint32_t *p_size)
95 {
96 	list_add_tail(node, &list->anchor);
97 	list->count++;
98 	*p_size = list->count;
99 	return QDF_STATUS_SUCCESS;
100 }
101 qdf_export_symbol(qdf_list_insert_back_size);
102 
103 /**
104  * qdf_list_remove_front() - remove node from front of the list
105  * @list: Pointer to list
106  * @node2: Double pointer to store the node which is removed from list
107  *
108  * Return: QDF status
109  */
qdf_list_remove_front(qdf_list_t * list,qdf_list_node_t ** node2)110 QDF_STATUS qdf_list_remove_front(qdf_list_t *list, qdf_list_node_t **node2)
111 {
112 	struct list_head *listptr;
113 
114 	if (list_empty(&list->anchor))
115 		return QDF_STATUS_E_EMPTY;
116 
117 	listptr = list->anchor.next;
118 	*node2 = listptr;
119 	list_del_init(list->anchor.next);
120 	list->count--;
121 
122 	return QDF_STATUS_SUCCESS;
123 }
124 qdf_export_symbol(qdf_list_remove_front);
125 
126 /**
127  * qdf_list_remove_back() - remove node from end of the list
128  * @list: Pointer to list
129  * @node2: Double pointer to store node which is removed from list
130  *
131  * Return: QDF status
132  */
qdf_list_remove_back(qdf_list_t * list,qdf_list_node_t ** node2)133 QDF_STATUS qdf_list_remove_back(qdf_list_t *list, qdf_list_node_t **node2)
134 {
135 	struct list_head *listptr;
136 
137 	if (list_empty(&list->anchor))
138 		return QDF_STATUS_E_EMPTY;
139 
140 	listptr = list->anchor.prev;
141 	*node2 = listptr;
142 	list_del_init(list->anchor.prev);
143 	list->count--;
144 
145 	return QDF_STATUS_SUCCESS;
146 }
147 qdf_export_symbol(qdf_list_remove_back);
148 
qdf_list_has_node(qdf_list_t * list,qdf_list_node_t * node)149 bool qdf_list_has_node(qdf_list_t *list, qdf_list_node_t *node)
150 {
151 	qdf_list_node_t *tmp;
152 
153 	list_for_each(tmp, &list->anchor) {
154 		if (tmp == node)
155 			return true;
156 	}
157 
158 	return false;
159 }
160 
161 /**
162  * qdf_list_remove_node() - remove input node from list
163  * @list: Pointer to list
164  * @node_to_remove: Pointer to node which needs to be removed
165  *
166  * verifies that the node is in the list before removing it.
167  * It is expected that the list being removed from is locked
168  * when this function is being called.
169  *
170  * Return: QDF status
171  */
qdf_list_remove_node(qdf_list_t * list,qdf_list_node_t * node_to_remove)172 QDF_STATUS qdf_list_remove_node(qdf_list_t *list,
173 				qdf_list_node_t *node_to_remove)
174 {
175 	if (list_empty(&list->anchor))
176 		return QDF_STATUS_E_EMPTY;
177 
178 	list_del_init(node_to_remove);
179 	list->count--;
180 
181 	return QDF_STATUS_SUCCESS;
182 }
183 qdf_export_symbol(qdf_list_remove_node);
184 
185 /**
186  * qdf_list_peek_front() - peek front node from list
187  * @list: Pointer to list
188  * @node2: Double pointer to store peeked node pointer
189  *
190  * Return: QDF status
191  */
qdf_list_peek_front(qdf_list_t * list,qdf_list_node_t ** node2)192 QDF_STATUS qdf_list_peek_front(qdf_list_t *list, qdf_list_node_t **node2)
193 {
194 	struct list_head *listptr;
195 
196 	if (list_empty(&list->anchor))
197 		return QDF_STATUS_E_EMPTY;
198 
199 	listptr = list->anchor.next;
200 	*node2 = listptr;
201 
202 	return QDF_STATUS_SUCCESS;
203 }
204 qdf_export_symbol(qdf_list_peek_front);
205 
206 /**
207  * qdf_list_peek_next() - peek next node of input node in the list
208  * @list: Pointer to list
209  * @node: Pointer to input node
210  * @node2: Double pointer to store peeked node pointer
211  *
212  * Return: QDF status
213  */
qdf_list_peek_next(qdf_list_t * list,qdf_list_node_t * node,qdf_list_node_t ** node2)214 QDF_STATUS qdf_list_peek_next(qdf_list_t *list,
215 			      qdf_list_node_t *node,
216 			      qdf_list_node_t **node2)
217 {
218 	if (!list || !node || !node2)
219 		return QDF_STATUS_E_FAULT;
220 
221 	if (list_empty(&list->anchor))
222 		return QDF_STATUS_E_EMPTY;
223 
224 	if (node->next == &list->anchor)
225 		return QDF_STATUS_E_EMPTY;
226 
227 	*node2 = node->next;
228 
229 	return QDF_STATUS_SUCCESS;
230 }
231 qdf_export_symbol(qdf_list_peek_next);
232 
233 /**
234  * qdf_list_empty() - check if the list is empty
235  * @list: pointer to the list
236  *
237  * Return: true if the list is empty and false otherwise.
238  */
qdf_list_empty(qdf_list_t * list)239 bool qdf_list_empty(qdf_list_t *list)
240 {
241 	return list_empty(&list->anchor);
242 }
243 qdf_export_symbol(qdf_list_empty);
244 
qdf_list_node_in_any_list(const qdf_list_node_t * node)245 bool qdf_list_node_in_any_list(const qdf_list_node_t *node)
246 {
247 	const struct list_head *linux_node = (const struct list_head *)node;
248 
249 	if (!linux_node)
250 		return false;
251 
252 	/* if the node is an empty list, it is not tied to an anchor node */
253 	if (list_empty(linux_node))
254 		return false;
255 
256 	if (!linux_node->prev || !linux_node->next)
257 		return false;
258 
259 	if (linux_node->prev->next != linux_node ||
260 	    linux_node->next->prev != linux_node)
261 		return false;
262 
263 	return true;
264 }
265 
qdf_list_split(qdf_list_t * new,qdf_list_t * list,qdf_list_node_t * node)266 QDF_STATUS qdf_list_split(qdf_list_t *new, qdf_list_t *list,
267 			  qdf_list_node_t *node)
268 {
269 	qdf_list_node_t *cur_node;
270 	uint32_t new_list_count = 0;
271 
272 	list_cut_position(&new->anchor, &list->anchor, node);
273 
274 	list_for_each(cur_node, &new->anchor)
275 		new_list_count++;
276 
277 	new->count = new_list_count;
278 	list->count = list->count - new->count;
279 
280 	return QDF_STATUS_SUCCESS;
281 }
282 
283 qdf_export_symbol(qdf_list_split);
284 
qdf_list_join(qdf_list_t * list1,qdf_list_t * list2)285 QDF_STATUS qdf_list_join(qdf_list_t *list1, qdf_list_t *list2)
286 {
287 	list_splice_tail_init(&list2->anchor, &list1->anchor);
288 
289 	list1->count = list1->count + list2->count;
290 	list2->count = 0;
291 
292 	return QDF_STATUS_SUCCESS;
293 }
294 
295 qdf_export_symbol(qdf_list_join);
296