|
@@ -0,0 +1,199 @@
|
|
|
+/*
|
|
|
+ * Copyright (c) 2019 The Linux Foundation. All rights reserved.
|
|
|
+ *
|
|
|
+ * 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.
|
|
|
+ */
|
|
|
+
|
|
|
+/**
|
|
|
+ * DOC: qdf_slist.h
|
|
|
+ *
|
|
|
+ * A minimal, singly linked list implementation, with push front, pop front, and
|
|
|
+ * remove capabilities. These are all O(1) operations.
|
|
|
+ *
|
|
|
+ * In order to remove an item, a pointer to the previous item must be known.
|
|
|
+ * Thus, removing an item is most efficient when combined with
|
|
|
+ * qdf_slist_for_each_del(). For cases where you need efficient removal of an
|
|
|
+ * arbitrary list node without iteration, consider using the doubly linked list
|
|
|
+ * qdf_list instead.
|
|
|
+ */
|
|
|
+
|
|
|
+#ifndef __QDF_SLIST_H
|
|
|
+#define __QDF_SLIST_H
|
|
|
+
|
|
|
+#include "qdf_trace.h"
|
|
|
+#include "qdf_util.h"
|
|
|
+
|
|
|
+#define __qdf_slist_poison ((void *)0xdeaddeaddeaddeadull)
|
|
|
+
|
|
|
+/**
|
|
|
+ * struct qdf_slist - a singly linked list
|
|
|
+ * @head: pointer to the head of the list
|
|
|
+ */
|
|
|
+struct qdf_slist {
|
|
|
+ struct qdf_slist_node *head;
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ * struct qdf_slist_node - a singly linked list node
|
|
|
+ * @next: pointer to the next node in the list, NULL if there is none
|
|
|
+ */
|
|
|
+struct qdf_slist_node {
|
|
|
+ struct qdf_slist_node *next;
|
|
|
+};
|
|
|
+
|
|
|
+#define __qdf_slist_item(node, cursor, node_field) ({ \
|
|
|
+ struct qdf_slist_node *__n = (node); \
|
|
|
+ (__n ? qdf_container_of(__n, typeof(*(cursor)), node_field) : NULL); })
|
|
|
+
|
|
|
+#define __qdf_slist_next_item(slist, cursor, node_field) \
|
|
|
+ __qdf_slist_item(cursor ? (cursor)->node_field.next : \
|
|
|
+ (slist)->head, cursor, node_field)
|
|
|
+
|
|
|
+/**
|
|
|
+ * qdf_slist_for_each - iterate over all of the items in @slist
|
|
|
+ * @slist: pointer to the qdf_slist to iterate over
|
|
|
+ * @cursor: cursor pointer of the list's item type, populated for each item
|
|
|
+ * @node_field: name of the qdf_slist_node field in the item's type
|
|
|
+ */
|
|
|
+#define qdf_slist_for_each(slist, cursor, node_field) \
|
|
|
+ for (cursor = __qdf_slist_item((slist)->head, cursor, node_field); \
|
|
|
+ cursor; \
|
|
|
+ cursor = __qdf_slist_item((cursor)->node_field.next, \
|
|
|
+ cursor, node_field))
|
|
|
+
|
|
|
+/**
|
|
|
+ * qdf_slist_for_each_del - iterate over all of the items in @slist,
|
|
|
+ * allowing for the safe deletion of nodes during iteration
|
|
|
+ * @slist: pointer to the qdf_slist to iterate over
|
|
|
+ * @prev: cursor pointer, populated with the previous item
|
|
|
+ * @cursor: cursor pointer of the list's item type, populated for each item
|
|
|
+ * @node_field: name of the qdf_slist_node field in the item's type
|
|
|
+ */
|
|
|
+#define qdf_slist_for_each_del(slist, prev, cursor, node_field) \
|
|
|
+ for (prev = NULL, \
|
|
|
+ cursor = __qdf_slist_item((slist)->head, cursor, node_field); \
|
|
|
+ cursor; \
|
|
|
+ prev = __qdf_slist_next_item(slist, prev, node_field) == \
|
|
|
+ cursor ? cursor : prev, \
|
|
|
+ cursor = __qdf_slist_next_item(slist, prev, node_field))
|
|
|
+
|
|
|
+/**
|
|
|
+ * qdf_slist_init() - initialize a qdf_slist
|
|
|
+ * @slist: the list to initialize
|
|
|
+ *
|
|
|
+ * Return: None
|
|
|
+ */
|
|
|
+static inline void qdf_slist_init(struct qdf_slist *slist)
|
|
|
+{
|
|
|
+ slist->head = NULL;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * qdf_slist_deinit() - deinitialize a qdf_slist
|
|
|
+ * @slist: the list to deinitialize
|
|
|
+ *
|
|
|
+ * Return: None
|
|
|
+ */
|
|
|
+static inline void qdf_slist_deinit(struct qdf_slist *slist)
|
|
|
+{
|
|
|
+ QDF_BUG(!slist->head);
|
|
|
+ slist->head = __qdf_slist_poison;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * qdf_slist_empty() - check if a qdf_slist is empty
|
|
|
+ * @slist: the list to check
|
|
|
+ *
|
|
|
+ * Return: true if @slist contains zero items
|
|
|
+ */
|
|
|
+static inline bool qdf_slist_empty(struct qdf_slist *slist)
|
|
|
+{
|
|
|
+ return !slist->head;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * qdf_slist_push() - push an item into the front of a qdf_slist
|
|
|
+ * @slist: the list to push into
|
|
|
+ * @cursor: the item to push
|
|
|
+ * @node_field: name of the qdf_slist_node field in the item's type
|
|
|
+ *
|
|
|
+ * Return: None
|
|
|
+ */
|
|
|
+#define qdf_slist_push(slist, cursor, node_field) \
|
|
|
+ __qdf_slist_push(slist, &(cursor)->node_field)
|
|
|
+
|
|
|
+static inline void
|
|
|
+__qdf_slist_push(struct qdf_slist *slist, struct qdf_slist_node *node)
|
|
|
+{
|
|
|
+ node->next = slist->head;
|
|
|
+ slist->head = node;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * qdf_slist_pop() - pop an item from the front of a qdf_slist
|
|
|
+ * @slist: the list to pop from
|
|
|
+ * @cursor: cursor pointer of the list's item type, not populated
|
|
|
+ * @node_field: name of the qdf_slist_node field in the item's type
|
|
|
+ *
|
|
|
+ * Return: pointer to the popped item, NULL if @slist was empty
|
|
|
+ */
|
|
|
+#define qdf_slist_pop(slist, cursor, node_field) \
|
|
|
+ __qdf_slist_item(__qdf_slist_pop(slist), cursor, node_field)
|
|
|
+
|
|
|
+static inline struct qdf_slist_node *__qdf_slist_pop(struct qdf_slist *slist)
|
|
|
+{
|
|
|
+ struct qdf_slist_node *node = slist->head;
|
|
|
+
|
|
|
+ if (!node)
|
|
|
+ return NULL;
|
|
|
+
|
|
|
+ slist->head = node->next;
|
|
|
+ node->next = __qdf_slist_poison;
|
|
|
+
|
|
|
+ return node;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * qdf_slist_remove() - remove an item from a qdf_slist
|
|
|
+ * @slist: the list to remove from
|
|
|
+ * @prev: pointer to the item previous to the item to remove, NULL removes head
|
|
|
+ * @node_field: name of the qdf_slist_node field in the item's type
|
|
|
+ *
|
|
|
+ * Return: pointer to the removed item, NULL if none was removed
|
|
|
+ */
|
|
|
+#define qdf_slist_remove(slist, prev, node_field) \
|
|
|
+ __qdf_slist_item(__qdf_slist_remove(slist, \
|
|
|
+ prev ? &(prev)->node_field : NULL), prev, node_field)
|
|
|
+
|
|
|
+static inline struct qdf_slist_node *
|
|
|
+__qdf_slist_remove(struct qdf_slist *slist, struct qdf_slist_node *prev)
|
|
|
+{
|
|
|
+ struct qdf_slist_node *node;
|
|
|
+
|
|
|
+ if (!prev)
|
|
|
+ return __qdf_slist_pop(slist);
|
|
|
+
|
|
|
+ if (!prev->next)
|
|
|
+ return NULL;
|
|
|
+
|
|
|
+ node = prev->next;
|
|
|
+ prev->next = node->next;
|
|
|
+ node->next = __qdf_slist_poison;
|
|
|
+
|
|
|
+ return node;
|
|
|
+}
|
|
|
+
|
|
|
+#endif /* __QDF_SLIST_H */
|
|
|
+
|