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
This commit is contained in:
Dustin Brown
2018-07-06 14:19:30 -07:00
committed by nshrivas
parent f88b7f861f
commit 424788cfe0
11 changed files with 1144 additions and 0 deletions

View File

@@ -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;
}