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:
247
components/dsc/src/__wlan_dsc.c
Normal file
247
components/dsc/src/__wlan_dsc.c
Normal 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;
|
||||
}
|
||||
|
Reference in New Issue
Block a user