Selaa lähdekoodia

qcacld-3.0: Add Driver Synchronization Core (common)

The Driver Synchronization Core (DSC) is a set of synchronization
primitives for use by the driver's orchestration layer. It provides APIs
for ensuring safe state transitions (including bring up and tear down)
of major driver objects: a single driver, associated psocs, and their
associated vdevs.

APIs are divided into two categories: mutual exclusion of conflicting
transitions, and operation tracking, blocking, and waiting capabilities.

For part 1, add common infrastructure and headers.

Change-Id: Id290e66d2dccd28b89fed5f285d3692ff3c814e7
CRs-Fixed: 2290260
Dustin Brown 6 vuotta sitten
vanhempi
sitoutus
424788cfe0

+ 55 - 0
components/dsc/inc/wlan_dsc.h

@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2018 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: Driver Synchronization Core (DSC) APIs for use by the driver
+ * orchestration layer.
+ *
+ * This infrastructure accomplishes two high level goals:
+ * 1) Replace ad-hoc locking/flags (hdd_init_deinit_lock,
+ *    iface_change_lock, con_mode_flag, etc., etc., etc.)
+ * 2) Make cds_ssr_protect() and driver state checking atomic
+ *
+ * These two goals are commplished in DSC via two corollary concepts:
+ * 1) Transitions (as in driver state machine transitions)
+ *    These are mutually exclusive, and replace ad-hoc locking
+ * 2) Operations (as in operations the driver is currently servicing)
+ *    These execute concurrently with other operations, and replace
+ *    cds_ssr_protect(). Any active transition causes new operations to be
+ *    rejected, in the same way as cds_ssr_protect/hdd_validate_context would.
+ *
+ * Transitions and operations are split into 3 distinct levels: driver, psoc,
+ * and vdev. These levels are arranged into a tree, with a single driver at
+ * the root, zero or more psocs per driver, and zero or more vdevs per psoc.
+ *
+ * High level transitions block transitions and operations at the same level
+ * down, and low level transitions block transitions at the same level up. So a
+ * driver transition effectively prevents any new activity in the system, while
+ * a vdev transition prevents transtitions on the same vdev, its parent psoc,
+ * and the driver. This also means that sibling nodes can transition at the same
+ * time, e.g. one vdev going up at the same time another is going down.
+ */
+
+#ifndef __WLAN_DSC_H
+#define __WLAN_DSC_H
+
+#include "wlan_dsc_driver.h"
+#include "wlan_dsc_psoc.h"
+#include "wlan_dsc_vdev.h"
+
+#endif /* __WLAN_DSC_H */

+ 141 - 0
components/dsc/inc/wlan_dsc_driver.h

@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2018 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: Driver Synchronization Core (DSC) driver-level APIs
+ */
+
+#ifndef __WLAN_DSC_DRIVER_H
+#define __WLAN_DSC_DRIVER_H
+
+#include "qdf_status.h"
+
+/**
+ * struct dsc_driver - opaque dsc driver context
+ */
+struct dsc_driver;
+
+/**
+ * dsc_driver_create() - create a dsc driver context
+ * @out_driver: opaque double pointer to assign the new context to
+ *
+ * Return: QDF_STATUS
+ */
+QDF_STATUS dsc_driver_create(struct dsc_driver **out_driver);
+
+/**
+ * dsc_driver_destroy() - destroy a dsc driver context
+ * @out_driver: opaque double pointer to context to destroy and NULL
+ *
+ * Note, this:
+ *	- aborts all queued transitions on @driver
+ *	- asserts @driver has no attached psoc's
+ *	- asserts @driver has no operations in flight
+ *
+ * Return: None
+ */
+void dsc_driver_destroy(struct dsc_driver **out_driver);
+
+/**
+ * dsc_driver_trans_start() - start a transition on @driver
+ * @driver: the driver to start a transition on
+ * @desc: a unique description of the transition to start
+ *
+ * This API immediately aborts if a transition on @driver is already in flight
+ *
+ * Call dsc_driver_trans_stop() to complete the transition.
+ *
+ * Return:
+ *	QDF_STATUS_SUCCESS - transition started succcessfully
+ *	QDF_STATUS_E_INVAL - invalid request (causes debug panic)
+ *	QDF_STATUS_E_AGAIN - transition cannot currently be started
+ *	QDF_STATUS_E_ALREADY - transition with @desc already in flight
+ */
+QDF_STATUS dsc_driver_trans_start(struct dsc_driver *driver, const char *desc);
+
+/**
+ * dsc_driver_trans_start_wait() - start a transition on @driver, blocking if a
+ *	transition is already in flight
+ * @driver: the driver to start a transition on
+ * @desc: a unique description of the transition to start
+ *
+ * Call dsc_driver_trans_stop() to complete the transition.
+ *
+ * Return:
+ *	QDF_STATUS_SUCCESS - transition started succcessfully
+ *	QDF_STATUS_E_INVAL - invalid request (causes debug panic)
+ *	QDF_STATUS_E_AGAIN - transition cannot currently be started
+ *	QDF_STATUS_E_ALREADY - transition with @desc already queued or in flight
+ */
+QDF_STATUS
+dsc_driver_trans_start_wait(struct dsc_driver *driver, const char *desc);
+
+/**
+ * dsc_driver_trans_stop() - complete current transition in flight on @driver
+ * @driver: the driver to complete the transition on
+ *
+ * Note: this asserts a transition is currently in flight on @driver
+ *
+ * Return: None
+ */
+void dsc_driver_trans_stop(struct dsc_driver *driver);
+
+/**
+ * dsc_driver_trans_assert() - assert transition in flight on @driver
+ * @driver: the driver to assert transition in flight on
+ *
+ * Return: None
+ */
+void dsc_driver_trans_assert(struct dsc_driver *driver);
+
+/**
+ * dsc_driver_op_start() - start an operation on @driver
+ * @driver: the driver to start an operation on
+ *
+ * Return:
+ *	QDF_STATUS_SUCCESS - operation started succcessfully
+ *	QDF_STATUS_E_INVAL - invalid request (causes debug panic)
+ *	QDF_STATUS_E_AGAIN - operation cannot currently be started
+ *	QDF_STATUS_E_NOMEM - out of memory
+ */
+#define dsc_driver_op_start(driver) _dsc_driver_op_start(driver, __func__)
+QDF_STATUS _dsc_driver_op_start(struct dsc_driver *driver, const char *func);
+
+/**
+ * dsc_driver_op_stop() - complete operation with matching @func on @driver
+ * @driver: the driver to stop an operation on
+ *
+ * Note: this asserts @func was previously started
+ *
+ * Return: None
+ */
+#define dsc_driver_op_stop(driver) _dsc_driver_op_stop(driver, __func__)
+void _dsc_driver_op_stop(struct dsc_driver *driver, const char *func);
+
+/**
+ * dsc_driver_wait_for_ops() - blocks until all operations on @driver have
+ *	stopped
+ * @driver: the driver to wait for operations on
+ *
+ * Note: this asserts that @driver cannot currently transition
+ *
+ * Return: None
+ */
+void dsc_driver_wait_for_ops(struct dsc_driver *driver);
+
+#endif /* __WLAN_DSC_DRIVER_H */

+ 145 - 0
components/dsc/inc/wlan_dsc_psoc.h

@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2018 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: Driver Synchronization Core (DSC) psoc-level APIs
+ */
+
+#ifndef __WLAN_DSC_PSOC_H
+#define __WLAN_DSC_PSOC_H
+
+#include "qdf_status.h"
+
+/**
+ * struct dsc_psoc - opaque dsc psoc context
+ */
+struct dsc_psoc;
+
+/**
+ * dsc_psoc_create() - create a dsc psoc context
+ * @driver: parent dsc driver context
+ * @out_psoc: opaque double pointer to assign the new context to
+ *
+ * Note: this attaches @out_psoc to @driver
+ *
+ * Return: QDF_STATUS
+ */
+QDF_STATUS
+dsc_psoc_create(struct dsc_driver *driver, struct dsc_psoc **out_psoc);
+
+/**
+ * dsc_psoc_destroy() - destroy a dsc psoc context
+ * @out_psoc: opaque double pointer to context to destroy and NULL
+ *
+ * Note, this:
+ *	- detaches @out_psoc from its parent driver context
+ *	- aborts all queued transitions on @psoc
+ *	- asserts @psoc has no attached vdev's
+ *	- asserts @psoc has no operations in flight
+ *
+ * Return: None
+ */
+void dsc_psoc_destroy(struct dsc_psoc **out_psoc);
+
+/**
+ * dsc_psoc_trans_start() - start a transition on @psoc
+ * @psoc: the psoc to start a transition on
+ * @desc: a unique description of the transition to start
+ *
+ * This API immediately aborts if a transition on @psoc is already in flight
+ *
+ * Call dsc_psoc_trans_stop() to complete the transition.
+ *
+ * Return:
+ *	QDF_STATUS_SUCCESS - transition started succcessfully
+ *	QDF_STATUS_E_INVAL - invalid request (causes debug panic)
+ *	QDF_STATUS_E_AGAIN - transition cannot currently be started
+ *	QDF_STATUS_E_ALREADY - transition with @desc already in flight
+ */
+QDF_STATUS dsc_psoc_trans_start(struct dsc_psoc *psoc, const char *desc);
+
+/**
+ * dsc_psoc_trans_start_wait() - start a transition on @psoc, blocking if a
+ *	transition is already in flight
+ * @psoc: the psoc to start a transition on
+ * @desc: a unique description of the transition to start
+ *
+ * Call dsc_psoc_trans_stop() to complete the transition.
+ *
+ * Return:
+ *	QDF_STATUS_SUCCESS - transition started succcessfully
+ *	QDF_STATUS_E_INVAL - invalid request (causes debug panic)
+ *	QDF_STATUS_E_AGAIN - transition cannot currently be started
+ *	QDF_STATUS_E_ALREADY - transition with @desc already queued or in flight
+ *	QDF_STATUS_E_ABORTED - transition was aborted
+ */
+QDF_STATUS dsc_psoc_trans_start_wait(struct dsc_psoc *psoc, const char *desc);
+
+/**
+ * dsc_psoc_trans_stop() - complete current transition in flight on @psoc
+ * @psoc: the psoc to complete the transition on
+ *
+ * Note: this asserts a transition is currently in flight on @psoc
+ *
+ * Return: None
+ */
+void dsc_psoc_trans_stop(struct dsc_psoc *psoc);
+
+/**
+ * dsc_psoc_trans_assert() - assert transition in flight on @psoc
+ * @psoc: the psoc to assert transition in flight on
+ *
+ * Return: None
+ */
+void dsc_psoc_trans_assert(struct dsc_psoc *psoc);
+
+/**
+ * dsc_psoc_op_start() - start an operation on @psoc
+ * @psoc: the psoc to start an operation on
+ *
+ * Return:
+ *	QDF_STATUS_SUCCESS - operation started succcessfully
+ *	QDF_STATUS_E_INVAL - invalid request (causes debug panic)
+ *	QDF_STATUS_E_AGAIN - operation cannot currently be started
+ *	QDF_STATUS_E_NOMEM - out of memory
+ */
+#define dsc_psoc_op_start(psoc) _dsc_psoc_op_start(psoc, __func__)
+QDF_STATUS _dsc_psoc_op_start(struct dsc_psoc *psoc, const char *func);
+
+/**
+ * dsc_psoc_op_stop() - complete operation with matching @func on @psoc
+ * @psoc: the psoc to stop an operation on
+ *
+ * Note: this asserts @func was previously started
+ *
+ * Return: None
+ */
+#define dsc_psoc_op_stop(psoc) _dsc_psoc_op_stop(psoc, __func__)
+void _dsc_psoc_op_stop(struct dsc_psoc *psoc, const char *func);
+
+/**
+ * dsc_psoc_wait_for_ops() - blocks until all operations on @psoc have stopped
+ * @psoc: the psoc to wait for operations on
+ *
+ * Note: this asserts that @psoc cannot currently transition
+ *
+ * Return: None
+ */
+void dsc_psoc_wait_for_ops(struct dsc_psoc *psoc);
+
+#endif /* __WLAN_DSC_PSOC_H */

+ 143 - 0
components/dsc/inc/wlan_dsc_vdev.h

@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2018 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: Driver Synchronization Core (DSC) vdev-level APIs
+ */
+
+#ifndef __WLAN_DSC_VDEV_H
+#define __WLAN_DSC_VDEV_H
+
+#include "qdf_status.h"
+
+/**
+ * struct dsc_vdev - opaque dsc vdev context
+ */
+struct dsc_vdev;
+
+/**
+ * dsc_vdev_create() - create a dsc vdev context
+ * @psoc: parent dsc psoc context
+ * @out_vdev: opaque double pointer to assign the new context to
+ *
+ * Note: this attaches @out_vdev to @psoc
+ *
+ * Return: QDF_STATUS
+ */
+QDF_STATUS dsc_vdev_create(struct dsc_psoc *psoc, struct dsc_vdev **out_vdev);
+
+/**
+ * dsc_vdev_destroy() - destroy a dsc vdev context
+ * @out_vdev: opaque double pointer to context to destroy and NULL
+ *
+ * Note, this:
+ *	- detaches @out_vdev from its parent psoc context
+ *	- aborts all queued transitions on @vdev
+ *	- asserts @vdev has no operations in flight
+ *
+ * Return: None
+ */
+void dsc_vdev_destroy(struct dsc_vdev **out_vdev);
+
+/**
+ * dsc_vdev_trans_start() - start a transition on @vdev
+ * @vdev: the vdev to start a transition on
+ * @desc: a unique description of the transition to start
+ *
+ * This API immediately aborts if a transition on @vdev is already in flight
+ *
+ * Call dsc_vdev_trans_stop() to complete the transition.
+ *
+ * Return:
+ *	QDF_STATUS_SUCCESS - transition started succcessfully
+ *	QDF_STATUS_E_INVAL - invalid request (causes debug panic)
+ *	QDF_STATUS_E_AGAIN - transition cannot currently be started
+ *	QDF_STATUS_E_ALREADY - transition with @desc already in flight
+ */
+QDF_STATUS dsc_vdev_trans_start(struct dsc_vdev *vdev, const char *desc);
+
+/**
+ * dsc_vdev_trans_start_wait() - start a transition on @vdev, blocking if a
+ *	transition is already in flight
+ * @vdev: the vdev to start a transition on
+ * @desc: a unique description of the transition to start
+ *
+ * Call dsc_vdev_trans_stop() to complete the transition.
+ *
+ * Return:
+ *	QDF_STATUS_SUCCESS - transition started succcessfully
+ *	QDF_STATUS_E_INVAL - invalid request (causes debug panic)
+ *	QDF_STATUS_E_AGAIN - transition cannot currently be started
+ *	QDF_STATUS_E_ALREADY - transition with @desc already queued or in flight
+ *	QDF_STATUS_E_ABORTED - transition was aborted
+ */
+QDF_STATUS dsc_vdev_trans_start_wait(struct dsc_vdev *vdev, const char *desc);
+
+/**
+ * dsc_vdev_trans_stop() - complete current transition in flight on @vdev
+ * @vdev: the vdev to complete the transition on
+ *
+ * Note: this asserts a transition is currently in flight on @vdev
+ *
+ * Return: None
+ */
+void dsc_vdev_trans_stop(struct dsc_vdev *vdev);
+
+/**
+ * dsc_vdev_trans_assert() - assert transition in flight on @vdev
+ * @vdev: the vdev to assert transition in flight on
+ *
+ * Return: None
+ */
+void dsc_vdev_trans_assert(struct dsc_vdev *vdev);
+
+/**
+ * dsc_vdev_op_start() - start an operation on @vdev
+ * @vdev: the vdev to start an operation on
+ *
+ * Return:
+ *	QDF_STATUS_SUCCESS - operation started succcessfully
+ *	QDF_STATUS_E_INVAL - invalid request (causes debug panic)
+ *	QDF_STATUS_E_AGAIN - operation cannot currently be started
+ *	QDF_STATUS_E_NOMEM - out of memory
+ */
+#define dsc_vdev_op_start(vdev) _dsc_vdev_op_start(vdev, __func__)
+QDF_STATUS _dsc_vdev_op_start(struct dsc_vdev *vdev, const char *func);
+
+/**
+ * dsc_vdev_op_stop() - complete operation with matching @func on @vdev
+ * @vdev: the vdev to stop an operation on
+ *
+ * Note: this asserts @func was previously started
+ *
+ * Return: None
+ */
+#define dsc_vdev_op_stop(vdev) _dsc_vdev_op_stop(vdev, __func__)
+void _dsc_vdev_op_stop(struct dsc_vdev *vdev, const char *func);
+
+/**
+ * dsc_vdev_wait_for_ops() - blocks until all operations on @vdev have stopped
+ * @vdev: the vdev to wait for operations on
+ *
+ * Note: this asserts that @vdev cannot currently transition
+ *
+ * Return: None
+ */
+void dsc_vdev_wait_for_ops(struct dsc_vdev *vdev);
+
+#endif /* __WLAN_DSC_VDEV_H */

+ 247 - 0
components/dsc/src/__wlan_dsc.c

@@ -0,0 +1,247 @@
+/*
+ * Copyright (c) 2018 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.
+ */
+
+#include "qdf_list.h"
+#include "qdf_mem.h"
+#include "qdf_status.h"
+#include "qdf_str.h"
+#include "__wlan_dsc.h"
+
+#ifdef WLAN_DSC_DEBUG
+/**
+ * __dsc_dbg_ops_init() - initialize debug ops data structures
+ * @ops: the ops container to initialize
+ *
+ * Return: None
+ */
+static inline void __dsc_dbg_ops_init(struct dsc_ops *ops)
+{
+	qdf_list_create(&ops->list, 0);
+}
+
+/**
+ * __dsc_dbg_ops_deinit() - de-initialize debug ops data structures
+ * @ops: the ops container to de-initialize
+ *
+ * Return: None
+ */
+static inline void __dsc_dbg_ops_deinit(struct dsc_ops *ops)
+{
+	qdf_list_destroy(&ops->list);
+}
+
+/**
+ * __dsc_dbg_ops_insert() - insert @func into the debug information in @ops
+ * @ops: the ops container to insert into
+ * @func: the debug information to insert
+ *
+ * Return: QDF_STATUS
+ */
+static QDF_STATUS __dsc_dbg_ops_insert(struct dsc_ops *ops, const char *func)
+{
+	struct dsc_op *op;
+
+	op = qdf_mem_malloc(sizeof(*op));
+	if (!op)
+		return QDF_STATUS_E_NOMEM;
+
+	op->func = func;
+
+	qdf_list_insert_back(&ops->list, &op->node);
+
+	return QDF_STATUS_SUCCESS;
+}
+
+/**
+ * __dsc_dbg_ops_remove() - remove @func from the debug information in @ops
+ * @ops: the ops container to remove from
+ * @func: the debug information to remove
+ *
+ * Return: None
+ */
+static void __dsc_dbg_ops_remove(struct dsc_ops *ops, const char *func)
+{
+	struct dsc_op *op;
+
+	/* Global pending op depth is usually <=3. Use linear search for now */
+	qdf_list_for_each(&ops->list, op, node) {
+		if (!qdf_str_eq(op->func, func))
+			continue;
+
+		/* this is safe because we cease iteration */
+		qdf_list_remove_node(&ops->list, &op->node);
+
+		qdf_mem_free(op);
+
+		return;
+	}
+
+	QDF_DEBUG_PANIC("Driver op '%s' is not pending", func);
+}
+#else
+static inline void __dsc_dbg_ops_init(struct dsc_ops *ops) { }
+
+static inline void __dsc_dbg_ops_deinit(struct dsc_ops *ops) { }
+
+static inline QDF_STATUS
+__dsc_dbg_ops_insert(struct dsc_ops *ops, const char *func)
+{
+	return QDF_STATUS_SUCCESS;
+}
+
+static inline void
+__dsc_dbg_ops_remove(struct dsc_ops *ops, const char *func) { }
+#endif /* WLAN_DSC_DEBUG */
+
+void __dsc_ops_init(struct dsc_ops *ops)
+{
+	ops->count = 0;
+	qdf_event_create(&ops->event);
+	__dsc_dbg_ops_init(ops);
+}
+
+void __dsc_ops_deinit(struct dsc_ops *ops)
+{
+	/* assert no ops in flight */
+	dsc_assert(!ops->count);
+
+	__dsc_dbg_ops_deinit(ops);
+	qdf_event_destroy(&ops->event);
+}
+
+QDF_STATUS __dsc_ops_insert(struct dsc_ops *ops, const char *func)
+{
+	QDF_STATUS status;
+
+	status = __dsc_dbg_ops_insert(ops, func);
+	if (QDF_IS_STATUS_ERROR(status))
+		return status;
+
+	ops->count++;
+
+	return QDF_STATUS_SUCCESS;
+}
+
+bool __dsc_ops_remove(struct dsc_ops *ops, const char *func)
+{
+	dsc_assert(ops->count);
+	ops->count--;
+
+	__dsc_dbg_ops_remove(ops, func);
+
+	return ops->count == 0;
+}
+
+void __dsc_trans_init(struct dsc_trans *trans)
+{
+	trans->active_desc = NULL;
+	qdf_list_create(&trans->queue, 0);
+}
+
+void __dsc_trans_deinit(struct dsc_trans *trans)
+{
+	qdf_list_destroy(&trans->queue);
+	trans->active_desc = NULL;
+}
+
+void __dsc_trans_queue(struct dsc_trans *trans, struct dsc_tran *tran,
+		       const char *desc)
+{
+	tran->abort = false;
+	tran->desc = desc;
+	qdf_event_create(&tran->event);
+	qdf_list_insert_back(&trans->queue, &tran->node);
+}
+
+/**
+ * __dsc_trans_dequeue() - dequeue the next queued transition from @trans
+ * @trans: the transactions container to dequeue from
+ *
+ * Return: the dequeued transition, or NULL if @trans is empty
+ */
+static struct dsc_tran *__dsc_trans_dequeue(struct dsc_trans *trans)
+{
+	QDF_STATUS status;
+	qdf_list_node_t *node;
+
+	status = qdf_list_remove_front(&trans->queue, &node);
+	if (QDF_IS_STATUS_ERROR(status))
+		return NULL;
+
+	return qdf_container_of(node, struct dsc_tran, node);
+}
+
+bool __dsc_trans_abort(struct dsc_trans *trans)
+{
+	struct dsc_tran *tran;
+
+	tran = __dsc_trans_dequeue(trans);
+	if (!tran)
+		return false;
+
+	tran->abort = true;
+	qdf_event_set(&tran->event);
+
+	return true;
+}
+
+bool __dsc_trans_trigger(struct dsc_trans *trans)
+{
+	struct dsc_tran *tran;
+
+	tran = __dsc_trans_dequeue(trans);
+	if (!tran)
+		return false;
+
+	trans->active_desc = tran->desc;
+	qdf_event_set(&tran->event);
+
+	return true;
+}
+
+bool __dsc_trans_active(struct dsc_trans *trans)
+{
+	return !!trans->active_desc;
+}
+
+bool __dsc_trans_queued(struct dsc_trans *trans)
+{
+	return !qdf_list_empty(&trans->queue);
+}
+
+bool __dsc_trans_active_or_queued(struct dsc_trans *trans)
+{
+	return __dsc_trans_active(trans) || __dsc_trans_queued(trans);
+}
+
+QDF_STATUS __dsc_tran_wait(struct dsc_tran *tran)
+{
+	QDF_STATUS status;
+
+	status = qdf_wait_single_event(&tran->event, DSC_TRANS_TIMEOUT);
+	qdf_event_destroy(&tran->event);
+
+	if (QDF_IS_STATUS_ERROR(status))
+		return status;
+
+	if (tran->abort)
+		return QDF_STATUS_E_ABORTED;
+
+	return QDF_STATUS_SUCCESS;
+}
+

+ 304 - 0
components/dsc/src/__wlan_dsc.h

@@ -0,0 +1,304 @@
+/*
+ * Copyright (c) 2018 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: Driver State Management (DSC) APIs for *internal* use
+ */
+
+#ifndef ____WLAN_DSC_H
+#define ____WLAN_DSC_H
+
+#include "qdf_event.h"
+#include "qdf_list.h"
+#include "qdf_trace.h"
+#include "qdf_types.h"
+#include "wlan_dsc.h"
+
+#define dsc_err(params...) QDF_TRACE_ERROR(QDF_MODULE_ID_QDF, params)
+#define dsc_info(params...) QDF_TRACE_INFO(QDF_MODULE_ID_QDF, params)
+#define dsc_debug(params...) QDF_TRACE_DEBUG(QDF_MODULE_ID_QDF, params)
+
+#define dsc_enter_exit dsc_debug
+#define dsc_enter() dsc_enter_exit("enter")
+#define dsc_enter_str(str) dsc_enter_exit("enter(\"%s\")", str)
+#define dsc_exit() dsc_enter_exit("exit")
+#define dsc_exit_status(status) dsc_enter_exit("exit(status:%u)", status)
+
+static inline bool __dsc_assert(const bool cond, const char *cond_str,
+				const char *func, const uint32_t line)
+{
+	if (cond)
+		return true;
+
+	QDF_DEBUG_PANIC_FL(func, line, "Failed assertion '%s'!", cond_str);
+
+	return false;
+}
+
+#define dsc_assert(cond) __dsc_assert(cond, #cond, __func__, __LINE__)
+#define dsc_assert_success(status) dsc_assert(QDF_IS_STATUS_SUCCESS(status))
+
+#ifdef WLAN_DSC_DEBUG
+#define DSC_TRANS_TIMEOUT 120000 /* 2 minutes */
+#else
+#define DSC_TRANS_TIMEOUT 0 /* no timeout */
+#endif
+
+#ifdef WLAN_DSC_DEBUG
+/**
+ * struct dsc_op - list node for operation tracking information
+ * @node: list node
+ * @func: name of the function the operation was started from
+ */
+struct dsc_op {
+	qdf_list_node_t node;
+	const char *func;
+};
+#endif /* WLAN_DSC_DEBUG */
+
+/**
+ * struct dsc_ops - operations in flight tracking container
+ * @list: list for tracking debug information
+ * @count: count of current operations in flight
+ * @event: event used to wait in *_wait_for_ops() APIs
+ */
+struct dsc_ops {
+#ifdef WLAN_DSC_DEBUG
+	qdf_list_t list;
+#endif
+	uint32_t count;
+	qdf_event_t event;
+};
+
+/**
+ * struct dsc_pending_trans - representation of a pending transition
+ * @abort: used to indicate if the transition stopped waiting due to an abort
+ * @desc: unique description of the transition
+ * @node: list node
+ * @event: event used to wait in *_start_trans_wait() APIs
+ */
+struct dsc_tran {
+	bool abort;
+	const char *desc;
+	qdf_list_node_t node;
+	qdf_event_t event;
+};
+
+/**
+ * struct dsc_trans - transition information container
+ * @active_desc: unique description of the current transition in progress
+ * @queue: queue of pending transitions
+ */
+struct dsc_trans {
+	const char *active_desc;
+	qdf_list_t queue;
+};
+
+/**
+ * struct dsc_driver - concrete dsc driver context
+ * @lock: lock under which all dsc APIs execute
+ * @psocs: list of children psoc contexts
+ * @trans: transition tracking container for this node
+ * @ops: operations in flight tracking container for this node
+ */
+struct dsc_driver {
+	struct qdf_spinlock lock;
+	qdf_list_t psocs;
+	struct dsc_trans trans;
+	struct dsc_ops ops;
+};
+
+/**
+ * struct dsc_psoc - concrete dsc psoc context
+ * @node: list node for membership in @driver->psocs
+ * @driver: parent driver context
+ * @vdevs: list of children vdevs contexts
+ * @trans: transition tracking container for this node
+ * @ops: operations in flight tracking container for this node
+ */
+struct dsc_psoc {
+	qdf_list_node_t node;
+	struct dsc_driver *driver;
+	qdf_list_t vdevs;
+	struct dsc_trans trans;
+	struct dsc_ops ops;
+};
+
+/**
+ * struct dsc_vdev - concrete dsc vdev context
+ * @node: list node for membership in @psoc->vdevs
+ * @psoc: parent psoc context
+ * @trans: transition tracking container for this node
+ * @ops: operations in flight tracking container for this node
+ */
+struct dsc_vdev {
+	qdf_list_node_t node;
+	struct dsc_psoc *psoc;
+	struct dsc_trans trans;
+	struct dsc_ops ops;
+};
+
+#define dsc_for_each_driver_psoc(driver_ptr, psoc_cursor) \
+	qdf_list_for_each(&(driver_ptr)->psocs, psoc_cursor, node)
+
+#define dsc_for_each_psoc_vdev(psoc_ptr, vdev_cursor) \
+	qdf_list_for_each(&(psoc_ptr)->vdevs, vdev_cursor, node)
+
+/**
+ * __dsc_lock() - grab the dsc global lock
+ *
+ * Return: None
+ */
+void __dsc_lock(void);
+
+/**
+ * __dsc_unlock() - release the dsc global lock
+ *
+ * Return: None
+ */
+void __dsc_unlock(void);
+
+/**
+ * __dsc_ops_init() - initialize @ops
+ * @ops: the ops container to initialize
+ *
+ * Return: None
+ */
+void __dsc_ops_init(struct dsc_ops *ops);
+
+/**
+ * __dsc_ops_deinit() - de-initialize @ops
+ * @ops: the ops container to de-initialize
+ *
+ * Return: None
+ */
+void __dsc_ops_deinit(struct dsc_ops *ops);
+
+/**
+ * __dsc_ops_insert() - insert @func into the trakcing information in @ops
+ * @ops: the ops container to insert into
+ * @func: the debug information to insert
+ *
+ * Return: QDF_STATUS
+ */
+QDF_STATUS __dsc_ops_insert(struct dsc_ops *ops, const char *func);
+
+/**
+ * __dsc_ops_remove() - remove @func from the tracking information in @ops
+ * @ops: the ops container to remove from
+ * @func: the debug information to remove
+ *
+ * Return: None
+ */
+bool __dsc_ops_remove(struct dsc_ops *ops, const char *func);
+
+/**
+ * __dsc_trans_init() - initialize @trans
+ * @trans: the trans container to initialize
+ *
+ * Return: None
+ */
+void __dsc_trans_init(struct dsc_trans *trans);
+
+/**
+ * __dsc_trans_deinit() - de-initialize @trans
+ * @trans: the trans container to de-initialize
+ *
+ * Return: None
+ */
+void __dsc_trans_deinit(struct dsc_trans *trans);
+
+/**
+ * __dsc_trans_queue() - queue @tran at the back of @trans
+ * @trans: the transitions container to enqueue to
+ * @tran: the transition to enqueue
+ * @desc: unique description of the transition being queued
+ *
+ * Return: None
+ */
+void __dsc_trans_queue(struct dsc_trans *trans, struct dsc_tran *tran,
+		       const char *desc);
+
+/**
+ * __dsc_tran_wait() - block until @tran completes
+ * @tran: the transition to wait on
+ *
+ * Return: QDF_STATUS
+ */
+QDF_STATUS __dsc_tran_wait(struct dsc_tran *tran);
+
+/**
+ * __dsc_trans_abort() - abort the next queued transition from @trans
+ * @trans: the transitions container to abort from
+ *
+ * Return: true if a transition was aborted, false if @trans is empty
+ */
+bool __dsc_trans_abort(struct dsc_trans *trans);
+
+/**
+ * __dsc_trans_trigger() - trigger the next queued trans in @trans
+ * @trans: the transitions container to trigger from
+ *
+ * Return: true if a transition was triggered
+ */
+bool __dsc_trans_trigger(struct dsc_trans *trans);
+
+/**
+ * __dsc_trans_active() - check if a transition is active in @trans
+ * @trans: the transitions container to check
+ *
+ * Return: true if @trans has an active transition
+ */
+bool __dsc_trans_active(struct dsc_trans *trans);
+
+/**
+ * __dsc_trans_queued() - check if a transition is queued in @trans
+ * @trans: the transitions container to check
+ *
+ * Return: true if @trans has a queued transition
+ */
+bool __dsc_trans_queued(struct dsc_trans *trans);
+
+/**
+ * __dsc_trans_active_or_queued() - check if a transition is active or queued
+ *	in @trans
+ * @trans: the transitions container to check
+ *
+ * Return: true if @trans has an active or queued transition
+ */
+bool __dsc_trans_active_or_queued(struct dsc_trans *trans);
+
+/**
+ * __dsc_driver_trans_trigger_checked() - trigger any next pending driver
+ *	transition, only after passing the "can trans" check
+ *
+ * Return: true if the trigger was "handled." This indicates down-tree nodes
+ * should _not_ attempt to trigger a new transition.
+ */
+bool __dsc_driver_trans_trigger_checked(struct dsc_driver *driver);
+
+/**
+ * __dsc_psoc_trans_trigger_checked() - trigger any next pending psoc
+ *	transition, only after passing the "can trans" check
+ *
+ * Return: true if the trigger was "handled." This indicates down-tree nodes
+ * should _not_ attempt to trigger a new transition.
+ */
+bool __dsc_psoc_trans_trigger_checked(struct dsc_psoc *psoc);
+
+#endif /* ____WLAN_DSC_H */

+ 18 - 0
components/dsc/src/wlan_dsc_driver.c

@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2018 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.
+ */
+

+ 18 - 0
components/dsc/src/wlan_dsc_psoc.c

@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2018 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.
+ */
+

+ 18 - 0
components/dsc/src/wlan_dsc_vdev.c

@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2018 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.
+ */
+

+ 18 - 0
components/dsc/test/wlan_dsc_test.c

@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2018 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.
+ */
+

+ 37 - 0
components/dsc/test/wlan_dsc_test.h

@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2018 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.
+ */
+
+#ifndef __WLAN_DSC_TEST
+#define __WLAN_DSC_TEST
+
+#ifdef WLAN_DSC_TEST
+/**
+ * dsc_unit_test() - run the dsc unit test suite
+ *
+ * Return: number of failed test cases
+ */
+uint32_t dsc_unit_test(void);
+#else
+static inline uint32_t dsc_unit_test(void)
+{
+	return 0;
+}
+#endif /* WLAN_DSC_TEST */
+
+#endif /* __WLAN_DSC_TEST */
+