Currently driver returns EAGAIN as error code if vdev transition is not possible, driver is not able to process this vdev transition because some other PSOC/PDEV transition is going on. On receiving EAGAIN error code userspace retries the same operation immediately which again leads to failure as existing transition is still in progress. In current scenario, for STA+P2P case, if station is connected to some ap and for p2p if DUT becomes p2p go, now if SSR is triggered, host driver gets interface down for sta for change mac address as station gets disconnected as part of SSR and this interface down is cached as SSR is in progress. Once reinit gets complete, driver processes this cached interface down as part of SSR and in this process driver gets the change interface for p2p go, which it rejects the error code as EAGAIN from __dsc_vdev_can_trans as SSR is going on and change interface fails to get dsc op. When user space gets EAGAIN as error code it immediately tries the change interface again which driver again rejects as SSR is still in progress. Also, on receiving EAGAIN error code, userspace retries the change iface operation only once. To address above issue, return error code as EBUSY, on receiving EBUSY as erorr code, user space addds some wait before invoking the same operation again and also it tries this operation 10 times if it gets EBUSY as error code, this gives sufficient time to complete the ongoing transition at driver. Change-Id: I96ec94432e7624546363cda190abfc9970ab1eb9 CRs-Fixed: 3126558
630 行
15 KiB
C
630 行
15 KiB
C
/*
|
|
* Copyright (c) 2018-2020 The Linux Foundation. All rights reserved.
|
|
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. 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 "__wlan_dsc.h"
|
|
#include "qdf_event.h"
|
|
#include "qdf_threads.h"
|
|
#include "qdf_trace.h"
|
|
#include "qdf_types.h"
|
|
#include "wlan_dsc.h"
|
|
#include "wlan_dsc_test.h"
|
|
#include "cds_api.h"
|
|
|
|
#define dsc_driver_trans_start(driver) dsc_driver_trans_start(driver, __func__)
|
|
#define dsc_psoc_trans_start(psoc) dsc_psoc_trans_start(psoc, __func__)
|
|
#define dsc_vdev_trans_start(vdev) dsc_vdev_trans_start(vdev, __func__)
|
|
|
|
#define dsc_driver_trans_start_wait(driver) \
|
|
dsc_driver_trans_start_wait(driver, "")
|
|
#define dsc_psoc_trans_start_wait(psoc) \
|
|
dsc_psoc_trans_start_wait(psoc, __func__)
|
|
#define dsc_vdev_trans_start_wait(vdev) \
|
|
dsc_vdev_trans_start_wait(vdev, __func__)
|
|
|
|
static struct dsc_psoc *nth_psoc(struct dsc_driver *driver, int n)
|
|
{
|
|
struct dsc_psoc *psoc;
|
|
|
|
QDF_BUG(n > 0);
|
|
if (n <= 0)
|
|
return NULL;
|
|
|
|
dsc_for_each_driver_psoc(driver, psoc) {
|
|
n--;
|
|
if (n)
|
|
continue;
|
|
|
|
return psoc;
|
|
}
|
|
|
|
QDF_DEBUG_PANIC("Failed to find nth psoc: %d", n);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static struct dsc_vdev *nth_vdev(struct dsc_psoc *psoc, int n)
|
|
{
|
|
struct dsc_vdev *vdev;
|
|
|
|
QDF_BUG(n > 0);
|
|
if (n <= 0)
|
|
return NULL;
|
|
|
|
dsc_for_each_psoc_vdev(psoc, vdev) {
|
|
n--;
|
|
if (n)
|
|
continue;
|
|
|
|
return vdev;
|
|
}
|
|
|
|
QDF_DEBUG_PANIC("Failed to find nth vdev: %d", n);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void __dsc_tree_destroy(struct dsc_driver *driver)
|
|
{
|
|
struct dsc_psoc *psoc;
|
|
struct dsc_psoc *next_psoc;
|
|
|
|
QDF_BUG(driver);
|
|
|
|
qdf_list_for_each_del(&driver->psocs, psoc, next_psoc, node) {
|
|
struct dsc_vdev *vdev;
|
|
struct dsc_vdev *next_vdev;
|
|
|
|
qdf_list_for_each_del(&psoc->vdevs, vdev, next_vdev, node)
|
|
dsc_vdev_destroy(&vdev);
|
|
|
|
dsc_psoc_destroy(&psoc);
|
|
}
|
|
|
|
dsc_driver_destroy(&driver);
|
|
}
|
|
|
|
static QDF_STATUS __dsc_tree_create(struct dsc_driver **out_driver,
|
|
uint8_t psocs_per_driver,
|
|
uint8_t vdevs_per_psoc)
|
|
{
|
|
QDF_STATUS status;
|
|
struct dsc_driver *driver;
|
|
int i, j;
|
|
|
|
status = dsc_driver_create(&driver);
|
|
if (QDF_IS_STATUS_ERROR(status)) {
|
|
dsc_err("Failed to create driver; status:%u", status);
|
|
return status;
|
|
}
|
|
|
|
for (i = 0; i < psocs_per_driver; i++) {
|
|
struct dsc_psoc *psoc;
|
|
|
|
status = dsc_psoc_create(driver, &psoc);
|
|
if (QDF_IS_STATUS_ERROR(status)) {
|
|
dsc_err("Failed to create psoc; status:%u", status);
|
|
goto free_tree;
|
|
}
|
|
|
|
for (j = 0; j < vdevs_per_psoc; j++) {
|
|
struct dsc_vdev *vdev;
|
|
|
|
status = dsc_vdev_create(psoc, &vdev);
|
|
if (QDF_IS_STATUS_ERROR(status)) {
|
|
dsc_err("Failed to create vdev; status:%u",
|
|
status);
|
|
goto free_tree;
|
|
}
|
|
}
|
|
}
|
|
|
|
*out_driver = driver;
|
|
|
|
return QDF_STATUS_SUCCESS;
|
|
|
|
free_tree:
|
|
__dsc_tree_destroy(driver);
|
|
|
|
return status;
|
|
}
|
|
|
|
static uint32_t dsc_test_create_destroy(void)
|
|
{
|
|
uint32_t errors = 0;
|
|
QDF_STATUS status;
|
|
struct dsc_driver *driver;
|
|
|
|
dsc_enter();
|
|
|
|
status = __dsc_tree_create(&driver, 2, 2);
|
|
if (QDF_IS_STATUS_ERROR(status)) {
|
|
errors++;
|
|
goto exit;
|
|
}
|
|
|
|
__dsc_tree_destroy(driver);
|
|
|
|
exit:
|
|
dsc_exit();
|
|
|
|
return errors;
|
|
}
|
|
|
|
#define action_expect(obj, action, status, errors) \
|
|
do { \
|
|
void *__obj = obj; \
|
|
QDF_STATUS __expected = status; \
|
|
QDF_STATUS __result; \
|
|
\
|
|
__result = dsc_##obj##_##action##_start(__obj); \
|
|
if (__result != __expected) { \
|
|
dsc_err("FAIL: " #obj " " #action \
|
|
"; expected " #status " (%u), found %u", \
|
|
__expected, __result); \
|
|
(errors)++; \
|
|
} \
|
|
if (QDF_IS_STATUS_SUCCESS(__result) && QDF_IS_STATUS_ERROR(__expected))\
|
|
dsc_##obj##_##action##_stop(__obj); \
|
|
} while (false)
|
|
|
|
static uint32_t dsc_test_driver_trans_blocks(void)
|
|
{
|
|
uint32_t errors = 0;
|
|
QDF_STATUS status;
|
|
struct dsc_driver *driver;
|
|
struct dsc_psoc *psoc;
|
|
struct dsc_vdev *vdev;
|
|
|
|
dsc_enter();
|
|
|
|
/* setup */
|
|
|
|
status = __dsc_tree_create(&driver, 2, 2);
|
|
if (QDF_IS_STATUS_ERROR(status)) {
|
|
errors++;
|
|
goto exit;
|
|
}
|
|
|
|
/* test */
|
|
/* a driver in transition should cause ... */
|
|
action_expect(driver, trans, QDF_STATUS_SUCCESS, errors);
|
|
|
|
/* ... the same driver trans/ops to fail */
|
|
action_expect(driver, trans, QDF_STATUS_E_AGAIN, errors);
|
|
action_expect(driver, op, QDF_STATUS_E_AGAIN, errors);
|
|
|
|
/* ... children psoc trans/ops to fail */
|
|
dsc_for_each_driver_psoc(driver, psoc) {
|
|
action_expect(psoc, trans, QDF_STATUS_E_INVAL, errors);
|
|
action_expect(psoc, op, QDF_STATUS_E_INVAL, errors);
|
|
|
|
/* ... grandchildren vdev trans/ops to fail */
|
|
dsc_for_each_psoc_vdev(psoc, vdev) {
|
|
action_expect(vdev, trans, QDF_STATUS_E_INVAL, errors);
|
|
action_expect(vdev, op, QDF_STATUS_E_INVAL, errors);
|
|
}
|
|
}
|
|
|
|
/* teardown */
|
|
|
|
dsc_driver_trans_stop(driver);
|
|
|
|
__dsc_tree_destroy(driver);
|
|
|
|
exit:
|
|
dsc_exit();
|
|
|
|
return errors;
|
|
}
|
|
|
|
static uint32_t dsc_test_psoc_trans_blocks(void)
|
|
{
|
|
uint32_t errors = 0;
|
|
QDF_STATUS status;
|
|
struct dsc_driver *driver;
|
|
struct dsc_psoc *psoc;
|
|
struct dsc_vdev *vdev;
|
|
|
|
dsc_enter();
|
|
|
|
/* setup */
|
|
|
|
status = __dsc_tree_create(&driver, 2, 2);
|
|
if (QDF_IS_STATUS_ERROR(status)) {
|
|
errors++;
|
|
goto exit;
|
|
}
|
|
|
|
/* test */
|
|
/* a psoc in transition should cause ... */
|
|
psoc = nth_psoc(driver, 1);
|
|
action_expect(psoc, trans, QDF_STATUS_SUCCESS, errors);
|
|
|
|
/* ... driver trans/ops to fail */
|
|
action_expect(driver, trans, QDF_STATUS_E_AGAIN, errors);
|
|
action_expect(driver, op, QDF_STATUS_E_AGAIN, errors);
|
|
|
|
/* ... the same psoc trans/ops to fail */
|
|
action_expect(psoc, trans, QDF_STATUS_E_AGAIN, errors);
|
|
action_expect(psoc, op, QDF_STATUS_E_AGAIN, errors);
|
|
|
|
/* ... children vdev trans/ops to fail */
|
|
dsc_for_each_psoc_vdev(psoc, vdev) {
|
|
action_expect(vdev, trans, QDF_STATUS_E_BUSY, errors);
|
|
action_expect(vdev, op, QDF_STATUS_E_BUSY, errors);
|
|
}
|
|
|
|
/* ... while driver unload in progress vdev op and trans should be
|
|
* rejected with EINVAL
|
|
*/
|
|
cds_set_unload_in_progress(true);
|
|
dsc_for_each_psoc_vdev(psoc, vdev) {
|
|
action_expect(vdev, trans, QDF_STATUS_E_INVAL, errors);
|
|
action_expect(vdev, op, QDF_STATUS_E_INVAL, errors);
|
|
}
|
|
cds_set_unload_in_progress(false);
|
|
|
|
/* ... while SSR recovery in progress vdev op and trans should be
|
|
* rejected with EINVAL
|
|
*/
|
|
cds_set_recovery_in_progress(true);
|
|
dsc_for_each_psoc_vdev(psoc, vdev) {
|
|
action_expect(vdev, trans, QDF_STATUS_E_INVAL, errors);
|
|
action_expect(vdev, op, QDF_STATUS_E_INVAL, errors);
|
|
}
|
|
cds_set_recovery_in_progress(false);
|
|
|
|
/* a sibling psoc in transition should succeed and cause ... */
|
|
psoc = nth_psoc(driver, 2);
|
|
action_expect(psoc, trans, QDF_STATUS_SUCCESS, errors);
|
|
|
|
/* ... driver trans/ops to fail */
|
|
action_expect(driver, trans, QDF_STATUS_E_AGAIN, errors);
|
|
action_expect(driver, op, QDF_STATUS_E_AGAIN, errors);
|
|
|
|
/* ... the same psoc trans/ops to fail */
|
|
action_expect(psoc, trans, QDF_STATUS_E_AGAIN, errors);
|
|
action_expect(psoc, op, QDF_STATUS_E_AGAIN, errors);
|
|
|
|
/* ... children vdev trans/ops to fail */
|
|
dsc_for_each_psoc_vdev(psoc, vdev) {
|
|
action_expect(vdev, trans, QDF_STATUS_E_BUSY, errors);
|
|
action_expect(vdev, op, QDF_STATUS_E_BUSY, errors);
|
|
}
|
|
|
|
/* teardown */
|
|
|
|
dsc_for_each_driver_psoc(driver, psoc)
|
|
dsc_psoc_trans_stop(psoc);
|
|
|
|
__dsc_tree_destroy(driver);
|
|
|
|
exit:
|
|
dsc_exit();
|
|
|
|
return errors;
|
|
}
|
|
|
|
static uint32_t dsc_test_vdev_trans_blocks(void)
|
|
{
|
|
uint32_t errors = 0;
|
|
QDF_STATUS status;
|
|
struct dsc_driver *driver;
|
|
struct dsc_psoc *psoc;
|
|
struct dsc_vdev *vdev;
|
|
|
|
dsc_enter();
|
|
|
|
/* setup */
|
|
|
|
status = __dsc_tree_create(&driver, 2, 2);
|
|
if (QDF_IS_STATUS_ERROR(status)) {
|
|
errors++;
|
|
goto exit;
|
|
}
|
|
|
|
/* test */
|
|
|
|
/* a vdev in transition should cause ... */
|
|
dsc_for_each_driver_psoc(driver, psoc) {
|
|
dsc_for_each_psoc_vdev(psoc, vdev)
|
|
action_expect(vdev, trans, QDF_STATUS_SUCCESS, errors);
|
|
}
|
|
|
|
/* ... driver trans/ops to fail */
|
|
action_expect(driver, trans, QDF_STATUS_E_AGAIN, errors);
|
|
action_expect(driver, op, QDF_STATUS_E_AGAIN, errors);
|
|
|
|
/* ... psoc trans/ops to fail */
|
|
dsc_for_each_driver_psoc(driver, psoc) {
|
|
action_expect(psoc, trans, QDF_STATUS_E_AGAIN, errors);
|
|
action_expect(psoc, op, QDF_STATUS_E_AGAIN, errors);
|
|
|
|
/* ... the same vdev trans/ops to fail */
|
|
dsc_for_each_psoc_vdev(psoc, vdev) {
|
|
action_expect(vdev, trans, QDF_STATUS_E_BUSY, errors);
|
|
action_expect(vdev, op, QDF_STATUS_E_BUSY, errors);
|
|
}
|
|
}
|
|
|
|
/* teardown */
|
|
|
|
dsc_for_each_driver_psoc(driver, psoc) {
|
|
dsc_for_each_psoc_vdev(psoc, vdev)
|
|
dsc_vdev_trans_stop(vdev);
|
|
}
|
|
|
|
__dsc_tree_destroy(driver);
|
|
|
|
exit:
|
|
dsc_exit();
|
|
|
|
return errors;
|
|
}
|
|
|
|
#define THREAD_TIMEOUT 1000 /* ms */
|
|
#define dsc_event_wait(event) qdf_wait_single_event(event, THREAD_TIMEOUT)
|
|
|
|
#define step_assert(field, expected) \
|
|
do { \
|
|
uint32_t _step = ++(field); \
|
|
uint32_t _expected = (expected); \
|
|
\
|
|
if (_step != _expected) \
|
|
QDF_DEBUG_PANIC("Step count is %u; Expected %u", \
|
|
_step, _expected); \
|
|
} while (false)
|
|
|
|
#define trans_waiting(ctx) (!qdf_list_empty(&(ctx)->trans.queue))
|
|
|
|
struct thread_ctx {
|
|
struct dsc_driver *driver;
|
|
qdf_event_t start_vdev_trans;
|
|
qdf_event_t start_vdev_wait;
|
|
uint32_t step;
|
|
};
|
|
|
|
static QDF_STATUS dsc_thread_ops(void *context)
|
|
{
|
|
struct thread_ctx *ctx = context;
|
|
struct dsc_driver *driver = ctx->driver;
|
|
struct dsc_psoc *psoc = nth_psoc(driver, 1);
|
|
struct dsc_vdev *vdev = nth_vdev(psoc, 1);
|
|
|
|
dsc_enter();
|
|
|
|
/* thread 1 is doing some operations ... */
|
|
step_assert(ctx->step, 1);
|
|
dsc_assert_success(dsc_driver_op_start(driver));
|
|
dsc_assert_success(dsc_psoc_op_start(psoc));
|
|
dsc_assert_success(dsc_vdev_op_start(vdev));
|
|
step_assert(ctx->step, 2);
|
|
|
|
/* ... at which point, thread 2 starts to transition the vdevs */
|
|
qdf_event_set(&ctx->start_vdev_trans);
|
|
|
|
/* meanwhile, thread 3,4,5 queue up to transition vdev/psoc/driver */
|
|
while (!trans_waiting(driver))
|
|
schedule();
|
|
|
|
/* at this point, each thread is:
|
|
* 1) doing operations
|
|
* 2) transitioning vdevs 1/2, waiting for ops to finish
|
|
* 3) waiting to transition vdev 1
|
|
* 4) waiting to transition psoc
|
|
* 5) waitint to transition driver
|
|
*/
|
|
|
|
step_assert(ctx->step, 8);
|
|
dsc_driver_op_stop(driver);
|
|
schedule();
|
|
dsc_psoc_op_stop(psoc);
|
|
schedule();
|
|
dsc_vdev_op_stop(vdev);
|
|
|
|
/* all operations complete; thread2 is now unblocked */
|
|
|
|
dsc_exit();
|
|
|
|
return QDF_STATUS_SUCCESS;
|
|
}
|
|
|
|
static QDF_STATUS dsc_thread_vdev_trans(void *context)
|
|
{
|
|
struct thread_ctx *ctx = context;
|
|
struct dsc_driver *driver = ctx->driver;
|
|
struct dsc_psoc *psoc = nth_psoc(driver, 1);
|
|
struct dsc_vdev *vdev;
|
|
|
|
dsc_enter();
|
|
|
|
/* wait for thread 1 to start operations */
|
|
dsc_assert_success(dsc_event_wait(&ctx->start_vdev_trans));
|
|
|
|
/* start transitions on all vdevs */
|
|
step_assert(ctx->step, 3);
|
|
dsc_for_each_psoc_vdev(psoc, vdev)
|
|
dsc_assert_success(dsc_vdev_trans_start(vdev));
|
|
step_assert(ctx->step, 4);
|
|
|
|
/* meanwhile, thread 3,4,5 queue up to transition vdev/psoc/driver */
|
|
qdf_event_set(&ctx->start_vdev_wait);
|
|
|
|
/* wait for thread 1 to complete pending vdev ops */
|
|
dsc_for_each_psoc_vdev(psoc, vdev)
|
|
dsc_vdev_wait_for_ops(vdev);
|
|
|
|
/* actual vdev transition work would happen here */
|
|
|
|
/* stop transition on vdev 1 */
|
|
step_assert(ctx->step, 9);
|
|
dsc_vdev_trans_stop(nth_vdev(psoc, 1));
|
|
|
|
/* psoc trans should not start until both vdev trans are complete */
|
|
schedule();
|
|
step_assert(ctx->step, 10);
|
|
dsc_vdev_trans_stop(nth_vdev(psoc, 2));
|
|
|
|
dsc_exit();
|
|
|
|
return QDF_STATUS_SUCCESS;
|
|
}
|
|
|
|
static QDF_STATUS dsc_thread_vdev_wait(void *context)
|
|
{
|
|
struct thread_ctx *ctx = context;
|
|
struct dsc_vdev *vdev = nth_vdev(nth_psoc(ctx->driver, 1), 1);
|
|
|
|
dsc_enter();
|
|
|
|
dsc_assert_success(dsc_event_wait(&ctx->start_vdev_wait));
|
|
|
|
step_assert(ctx->step, 5);
|
|
/* vdev trans queues first ... */
|
|
dsc_assert_success(dsc_vdev_trans_start_wait(vdev));
|
|
/* ... but de-queues third */
|
|
step_assert(ctx->step, 15);
|
|
|
|
dsc_vdev_wait_for_ops(vdev);
|
|
|
|
step_assert(ctx->step, 16);
|
|
dsc_vdev_trans_stop(vdev);
|
|
|
|
dsc_exit();
|
|
|
|
return QDF_STATUS_SUCCESS;
|
|
}
|
|
|
|
static QDF_STATUS dsc_thread_psoc_wait(void *context)
|
|
{
|
|
struct thread_ctx *ctx = context;
|
|
struct dsc_psoc *psoc = nth_psoc(ctx->driver, 1);
|
|
struct dsc_vdev *vdev = nth_vdev(psoc, 1);
|
|
|
|
dsc_enter();
|
|
|
|
while (!trans_waiting(vdev))
|
|
schedule();
|
|
|
|
step_assert(ctx->step, 6);
|
|
/* psoc trans queues second ... */
|
|
dsc_assert_success(dsc_psoc_trans_start_wait(psoc));
|
|
/* ... and de-queues second */
|
|
step_assert(ctx->step, 13);
|
|
|
|
dsc_psoc_wait_for_ops(psoc);
|
|
|
|
step_assert(ctx->step, 14);
|
|
dsc_psoc_trans_stop(psoc);
|
|
|
|
dsc_exit();
|
|
|
|
return QDF_STATUS_SUCCESS;
|
|
}
|
|
|
|
static QDF_STATUS dsc_thread_driver_wait(void *context)
|
|
{
|
|
struct thread_ctx *ctx = context;
|
|
struct dsc_driver *driver = ctx->driver;
|
|
struct dsc_psoc *psoc = nth_psoc(driver, 1);
|
|
|
|
dsc_enter();
|
|
|
|
while (!trans_waiting(psoc))
|
|
schedule();
|
|
|
|
step_assert(ctx->step, 7);
|
|
/* driver trans queues third ... */
|
|
dsc_assert_success(dsc_driver_trans_start_wait(driver));
|
|
/* ... but de-queues first */
|
|
step_assert(ctx->step, 11);
|
|
|
|
dsc_driver_wait_for_ops(driver);
|
|
|
|
step_assert(ctx->step, 12);
|
|
dsc_driver_trans_stop(driver);
|
|
|
|
dsc_exit();
|
|
|
|
return QDF_STATUS_SUCCESS;
|
|
}
|
|
|
|
static uint32_t dsc_test_trans_wait(void)
|
|
{
|
|
uint32_t errors = 0;
|
|
QDF_STATUS status;
|
|
qdf_thread_t *ops_thread;
|
|
qdf_thread_t *vdev_trans_thread;
|
|
qdf_thread_t *vdev_wait_thread;
|
|
qdf_thread_t *psoc_wait_thread;
|
|
qdf_thread_t *driver_wait_thread;
|
|
struct thread_ctx ctx = { 0 };
|
|
|
|
dsc_enter();
|
|
|
|
status = __dsc_tree_create(&ctx.driver, 1, 2);
|
|
if (QDF_IS_STATUS_ERROR(status)) {
|
|
errors++;
|
|
goto exit;
|
|
}
|
|
|
|
dsc_assert_success(qdf_event_create(&ctx.start_vdev_trans));
|
|
dsc_assert_success(qdf_event_create(&ctx.start_vdev_wait));
|
|
|
|
dsc_debug("starting threads");
|
|
|
|
ops_thread = qdf_thread_run(dsc_thread_ops, &ctx);
|
|
vdev_trans_thread = qdf_thread_run(dsc_thread_vdev_trans, &ctx);
|
|
vdev_wait_thread = qdf_thread_run(dsc_thread_vdev_wait, &ctx);
|
|
psoc_wait_thread = qdf_thread_run(dsc_thread_psoc_wait, &ctx);
|
|
driver_wait_thread = qdf_thread_run(dsc_thread_driver_wait, &ctx);
|
|
|
|
qdf_thread_join(ops_thread);
|
|
qdf_thread_join(vdev_trans_thread);
|
|
qdf_thread_join(vdev_wait_thread);
|
|
qdf_thread_join(psoc_wait_thread);
|
|
qdf_thread_join(driver_wait_thread);
|
|
|
|
dsc_debug("threads joined");
|
|
|
|
qdf_event_destroy(&ctx.start_vdev_wait);
|
|
qdf_event_destroy(&ctx.start_vdev_trans);
|
|
|
|
__dsc_tree_destroy(ctx.driver);
|
|
|
|
exit:
|
|
dsc_exit();
|
|
|
|
return errors;
|
|
}
|
|
|
|
uint32_t dsc_unit_test(void)
|
|
{
|
|
uint32_t errors = 0;
|
|
|
|
errors += dsc_test_create_destroy();
|
|
errors += dsc_test_driver_trans_blocks();
|
|
errors += dsc_test_psoc_trans_blocks();
|
|
errors += dsc_test_vdev_trans_blocks();
|
|
errors += dsc_test_trans_wait();
|
|
|
|
return errors;
|
|
}
|
|
|