/*
 * Copyright (c) 2017 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_status.h"
#include "hif_main.h"
#include "hif_unit_test_suspend.h"
#include "hif_unit_test_suspend_i.h"

enum hif_ut_suspend_state_bits {
	UT_SUSPENDED_BIT = 0
};

/**
 * hif_ut_fw_resume_work() - Work handler for firmware-triggered resume
 * @work: The work struct being passed from the linux kernel
 *
 * Return: None
 */
static void hif_ut_fw_resume_work(struct work_struct *work)
{
	struct hif_ut_suspend_context *ctx =
		container_of(work, struct hif_ut_suspend_context, resume_work);

	QDF_BUG(ctx);
	if (!ctx)
		return;

	QDF_BUG(ctx->resume_callback);
	if (!ctx->resume_callback)
		return;

	ctx->resume_callback();
	ctx->resume_callback = NULL;
}

void hif_ut_suspend_init(struct hif_softc *scn)
{
	INIT_WORK(&scn->ut_suspend_ctx.resume_work, hif_ut_fw_resume_work);
}

bool hif_is_ut_suspended(struct hif_softc *scn)
{
	QDF_BUG(scn);
	if (!scn)
		return false;

	return test_bit(UT_SUSPENDED_BIT, &scn->ut_suspend_ctx.state);
}

QDF_STATUS hif_ut_apps_suspend(struct hif_opaque_softc *opaque_scn,
			       hif_ut_resume_callback callback)
{
	struct hif_softc *scn = HIF_GET_SOFTC(opaque_scn);

	QDF_BUG(scn);
	if (!scn)
		return QDF_STATUS_E_INVAL;

	QDF_BUG(callback);
	if (!callback)
		return QDF_STATUS_E_INVAL;

	if (test_and_set_bit(UT_SUSPENDED_BIT, &scn->ut_suspend_ctx.state))
		return QDF_STATUS_E_INVAL;

	scn->ut_suspend_ctx.resume_callback = callback;

	return QDF_STATUS_SUCCESS;
}

QDF_STATUS hif_ut_apps_resume(struct hif_opaque_softc *opaque_scn)
{
	struct hif_softc *scn = HIF_GET_SOFTC(opaque_scn);

	QDF_BUG(scn);
	if (!scn)
		return QDF_STATUS_E_INVAL;

	if (!test_and_clear_bit(UT_SUSPENDED_BIT, &scn->ut_suspend_ctx.state))
		return QDF_STATUS_E_INVAL;

	scn->ut_suspend_ctx.resume_callback = NULL;

	return QDF_STATUS_SUCCESS;
}

QDF_STATUS hif_ut_fw_resume(struct hif_softc *scn)
{
	QDF_BUG(scn);
	if (!scn)
		return QDF_STATUS_E_INVAL;

	if (!test_and_clear_bit(UT_SUSPENDED_BIT, &scn->ut_suspend_ctx.state))
		return QDF_STATUS_E_INVAL;

	schedule_work(&scn->ut_suspend_ctx.resume_work);

	return QDF_STATUS_SUCCESS;
}
