/*
 * Copyright (c) 2017-2020 The Linux Foundation. All rights reserved.
 * Copyright (c) 2021-2023 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.
 */

/**
 * DOC: This file contains main P2P function definitions
 */

#include <scheduler_api.h>
#include <wlan_objmgr_psoc_obj.h>
#include <wlan_objmgr_global_obj.h>
#include <wlan_objmgr_pdev_obj.h>
#include <wlan_objmgr_vdev_obj.h>
#include <wlan_objmgr_peer_obj.h>
#include <wlan_scan_api.h>
#include "wlan_p2p_public_struct.h"
#include "wlan_p2p_ucfg_api.h"
#include "wlan_p2p_tgt_api.h"
#include "wlan_p2p_mcc_quota_tgt_api.h"
#include "wlan_p2p_main.h"
#include "wlan_p2p_roc.h"
#include "wlan_p2p_off_chan_tx.h"
#include "cfg_p2p.h"
#include "cfg_ucfg_api.h"
#include "wlan_mlme_api.h"

/**
 * p2p_get_cmd_type_str() - parse cmd to string
 * @cmd_type: P2P cmd type
 *
 * This function parse P2P cmd to string.
 *
 * Return: command string
 */
static char *p2p_get_cmd_type_str(enum p2p_cmd_type cmd_type)
{
	switch (cmd_type) {
	case P2P_ROC_REQ:
		return "P2P roc request";
	case P2P_CANCEL_ROC_REQ:
		return "P2P cancel roc request";
	case P2P_MGMT_TX:
		return "P2P mgmt tx request";
	case P2P_MGMT_TX_CANCEL:
		return "P2P cancel mgmt tx request";
	case P2P_CLEANUP_ROC:
		return "P2P cleanup roc";
	case P2P_CLEANUP_TX:
		return "P2P cleanup tx";
	case P2P_SET_RANDOM_MAC:
		return "P2P set random mac";
	default:
		return "Invalid P2P command";
	}
}

/**
 * p2p_get_event_type_str() - parase event to string
 * @event_type: P2P event type
 *
 * This function parse P2P event to string.
 *
 * Return: event string
 */
static char *p2p_get_event_type_str(enum p2p_event_type event_type)
{
	switch (event_type) {
	case P2P_EVENT_SCAN_EVENT:
		return "P2P scan event";
	case P2P_EVENT_MGMT_TX_ACK_CNF:
		return "P2P mgmt tx ack event";
	case P2P_EVENT_RX_MGMT:
		return "P2P mgmt rx event";
	case P2P_EVENT_LO_STOPPED:
		return "P2P lo stop event";
	case P2P_EVENT_NOA:
		return "P2P noa event";
	case P2P_EVENT_ADD_MAC_RSP:
		return "P2P add mac filter resp event";
	default:
		return "Invalid P2P event";
	}
}

/**
 * p2p_psoc_obj_create_notification() - Function to allocate per P2P
 * soc private object
 * @soc: soc context
 * @data: Pointer to data
 *
 * This function gets called from object manager when psoc is being
 * created and creates p2p soc context.
 *
 * Return: QDF_STATUS_SUCCESS - in case of success
 */
static QDF_STATUS p2p_psoc_obj_create_notification(
	struct wlan_objmgr_psoc *soc, void *data)
{
	struct p2p_soc_priv_obj *p2p_soc_obj;
	QDF_STATUS status;

	if (!soc) {
		p2p_err("psoc context passed is NULL");
		return QDF_STATUS_E_INVAL;
	}

	p2p_soc_obj = qdf_mem_malloc(sizeof(*p2p_soc_obj));
	if (!p2p_soc_obj)
		return QDF_STATUS_E_NOMEM;

	p2p_soc_obj->soc = soc;

	status = wlan_objmgr_psoc_component_obj_attach(soc,
				WLAN_UMAC_COMP_P2P, p2p_soc_obj,
				QDF_STATUS_SUCCESS);
	if (status != QDF_STATUS_SUCCESS) {
		qdf_mem_free(p2p_soc_obj);
		p2p_err("Failed to attach p2p component, %d", status);
		return status;
	}

	p2p_debug("p2p soc object create successful, %pK", p2p_soc_obj);

	return QDF_STATUS_SUCCESS;
}

/**
 * p2p_psoc_obj_destroy_notification() - Free soc private object
 * @soc: soc context
 * @data: Pointer to data
 *
 * This function gets called from object manager when psoc is being
 * deleted and delete p2p soc context.
 *
 * Return: QDF_STATUS_SUCCESS - in case of success
 */
static QDF_STATUS p2p_psoc_obj_destroy_notification(
	struct wlan_objmgr_psoc *soc, void *data)
{
	struct p2p_soc_priv_obj *p2p_soc_obj;
	QDF_STATUS status;

	if (!soc) {
		p2p_err("psoc context passed is NULL");
		return QDF_STATUS_E_INVAL;
	}

	p2p_soc_obj = wlan_objmgr_psoc_get_comp_private_obj(soc,
			WLAN_UMAC_COMP_P2P);
	if (!p2p_soc_obj) {
		p2p_err("p2p soc private object is NULL");
		return QDF_STATUS_E_FAILURE;
	}

	p2p_soc_obj->soc = NULL;

	status = wlan_objmgr_psoc_component_obj_detach(soc,
				WLAN_UMAC_COMP_P2P, p2p_soc_obj);
	if (status != QDF_STATUS_SUCCESS) {
		p2p_err("Failed to detach p2p component, %d", status);
		return status;
	}

	p2p_debug("destroy p2p soc object, %pK", p2p_soc_obj);

	qdf_mem_free(p2p_soc_obj);

	return QDF_STATUS_SUCCESS;
}

/**
 * p2p_vdev_obj_create_notification() - Allocate per p2p vdev object
 * @vdev: vdev context
 * @data: Pointer to data
 *
 * This function gets called from object manager when vdev is being
 * created and creates p2p vdev context.
 *
 * Return: QDF_STATUS_SUCCESS - in case of success
 */
static QDF_STATUS p2p_vdev_obj_create_notification(
	struct wlan_objmgr_vdev *vdev, void *data)
{
	struct p2p_vdev_priv_obj *p2p_vdev_obj;
	QDF_STATUS status;
	enum QDF_OPMODE mode;

	if (!vdev) {
		p2p_err("vdev context passed is NULL");
		return QDF_STATUS_E_INVAL;
	}

	mode = wlan_vdev_mlme_get_opmode(vdev);
	p2p_debug("vdev mode:%d", mode);
	if (mode != QDF_P2P_GO_MODE &&
	    mode != QDF_STA_MODE &&
	    mode != QDF_P2P_CLIENT_MODE &&
	    mode != QDF_P2P_DEVICE_MODE) {
		p2p_debug("won't create p2p vdev private object for mode %d",
			  mode);
		return QDF_STATUS_SUCCESS;
	}

	p2p_vdev_obj =
		qdf_mem_malloc(sizeof(*p2p_vdev_obj));
	if (!p2p_vdev_obj)
		return QDF_STATUS_E_NOMEM;

	p2p_vdev_obj->vdev = vdev;
	p2p_vdev_obj->noa_status = true;
	p2p_vdev_obj->non_p2p_peer_count = 0;
	p2p_init_random_mac_vdev(p2p_vdev_obj);
	qdf_mem_copy(p2p_vdev_obj->prev_action_frame_addr2,
		     wlan_vdev_mlme_get_macaddr(vdev), QDF_MAC_ADDR_SIZE);

	status = wlan_objmgr_vdev_component_obj_attach(vdev,
				WLAN_UMAC_COMP_P2P, p2p_vdev_obj,
				QDF_STATUS_SUCCESS);
	if (status != QDF_STATUS_SUCCESS) {
		p2p_deinit_random_mac_vdev(p2p_vdev_obj);
		qdf_mem_free(p2p_vdev_obj);
		p2p_err("Failed to attach p2p component to vdev, %d",
			status);
		return status;
	}

	p2p_debug("p2p vdev object create successful, %pK", p2p_vdev_obj);

	return QDF_STATUS_SUCCESS;
}

/**
 * p2p_vdev_obj_destroy_notification() - Free per P2P vdev object
 * @vdev: vdev context
 * @data: Pointer to data
 *
 * This function gets called from object manager when vdev is being
 * deleted and delete p2p vdev context.
 *
 * Return: QDF_STATUS_SUCCESS - in case of success
 */
static QDF_STATUS p2p_vdev_obj_destroy_notification(
	struct wlan_objmgr_vdev *vdev, void *data)
{
	struct p2p_vdev_priv_obj *p2p_vdev_obj;
	QDF_STATUS status;
	enum QDF_OPMODE mode;

	if (!vdev) {
		p2p_err("vdev context passed is NULL");
		return QDF_STATUS_E_INVAL;
	}

	mode = wlan_vdev_mlme_get_opmode(vdev);
	p2p_debug("vdev mode:%d", mode);
	if (mode != QDF_P2P_GO_MODE &&
	    mode != QDF_STA_MODE &&
	    mode != QDF_P2P_CLIENT_MODE &&
	    mode != QDF_P2P_DEVICE_MODE){
		p2p_debug("no p2p vdev private object for mode %d", mode);
		return QDF_STATUS_SUCCESS;
	}

	p2p_vdev_obj = wlan_objmgr_vdev_get_comp_private_obj(vdev,
			WLAN_UMAC_COMP_P2P);
	if (!p2p_vdev_obj) {
		p2p_debug("p2p vdev object is NULL");
		return QDF_STATUS_SUCCESS;
	}
	p2p_deinit_random_mac_vdev(p2p_vdev_obj);

	p2p_vdev_obj->vdev = NULL;

	status = wlan_objmgr_vdev_component_obj_detach(vdev,
				WLAN_UMAC_COMP_P2P, p2p_vdev_obj);
	if (status != QDF_STATUS_SUCCESS) {
		p2p_err("Failed to detach p2p component, %d", status);
		return status;
	}

	p2p_debug("destroy p2p vdev object, p2p vdev obj:%pK, noa info:%pK",
		p2p_vdev_obj, p2p_vdev_obj->noa_info);

	if (p2p_vdev_obj->noa_info)
		qdf_mem_free(p2p_vdev_obj->noa_info);

	qdf_mem_free(p2p_vdev_obj);

	return QDF_STATUS_SUCCESS;
}

/**
 * p2p_peer_obj_create_notification() - manages peer details per vdev
 * @peer: peer object
 * @arg: Pointer to private argument - NULL
 *
 * This function gets called from object manager when peer is being
 * created.
 *
 * Return: QDF_STATUS_SUCCESS - in case of success
 */
static QDF_STATUS p2p_peer_obj_create_notification(
	struct wlan_objmgr_peer *peer, void *arg)
{
	struct wlan_objmgr_vdev *vdev;
	enum QDF_OPMODE mode;

	if (!peer) {
		p2p_err("peer context passed is NULL");
		return QDF_STATUS_E_INVAL;
	}

	vdev = wlan_peer_get_vdev(peer);
	mode = wlan_vdev_mlme_get_opmode(vdev);
	if (mode != QDF_P2P_GO_MODE)
		return QDF_STATUS_SUCCESS;

	p2p_debug("p2p peer object create successful");

	return QDF_STATUS_SUCCESS;
}

/**
 * p2p_peer_obj_destroy_notification() - clears peer details per vdev
 * @peer: peer object
 * @arg: Pointer to private argument - NULL
 *
 * This function gets called from object manager when peer is being
 * destroyed.
 *
 * Return: QDF_STATUS_SUCCESS - in case of success
 */
static QDF_STATUS p2p_peer_obj_destroy_notification(
	struct wlan_objmgr_peer *peer, void *arg)
{
	struct wlan_objmgr_vdev *vdev;
	struct p2p_vdev_priv_obj *p2p_vdev_obj;
	struct wlan_objmgr_psoc *psoc;
	enum QDF_OPMODE mode;
	enum wlan_peer_type peer_type;
	uint8_t vdev_id;

	if (!peer) {
		p2p_err("peer context passed is NULL");
		return QDF_STATUS_E_INVAL;
	}

	vdev = wlan_peer_get_vdev(peer);
	mode = wlan_vdev_mlme_get_opmode(vdev);
	if (mode != QDF_P2P_GO_MODE)
		return QDF_STATUS_SUCCESS;

	p2p_vdev_obj = wlan_objmgr_vdev_get_comp_private_obj(vdev,
						WLAN_UMAC_COMP_P2P);
	psoc = wlan_vdev_get_psoc(vdev);
	if (!p2p_vdev_obj || !psoc) {
		p2p_debug("p2p_vdev_obj:%pK psoc:%pK", p2p_vdev_obj, psoc);
		return QDF_STATUS_E_INVAL;
	}

	mode = wlan_vdev_mlme_get_opmode(vdev);

	peer_type = wlan_peer_get_peer_type(peer);

	if ((peer_type == WLAN_PEER_STA) && (mode == QDF_P2P_GO_MODE)) {

		p2p_vdev_obj->non_p2p_peer_count--;

		if (!p2p_vdev_obj->non_p2p_peer_count &&
		    (p2p_vdev_obj->noa_status == false)) {

			vdev_id = wlan_vdev_get_id(vdev);

			if (ucfg_p2p_set_noa(psoc, vdev_id,
				 false)	== QDF_STATUS_SUCCESS)
				p2p_vdev_obj->noa_status = true;
			else
				p2p_vdev_obj->noa_status = false;

			p2p_debug("Non p2p peer disconnected from GO,NOA status: %d.",
				p2p_vdev_obj->noa_status);
		}
		p2p_debug("Non P2P peer count: %d",
			   p2p_vdev_obj->non_p2p_peer_count);
	}
	p2p_debug("p2p peer object destroy successful");

	return QDF_STATUS_SUCCESS;
}

/**
 * p2p_send_noa_to_pe() - send noa information to pe
 * @noa_info: vdev context
 *
 * This function sends noa information to pe since MCL layer need noa
 * event.
 *
 * Return: QDF_STATUS_SUCCESS - in case of success
 */
static QDF_STATUS p2p_send_noa_to_pe(struct p2p_noa_info *noa_info)
{
	struct p2p_noa_attr *noa_attr;
	struct scheduler_msg msg = {0};
	QDF_STATUS status;

	if (!noa_info) {
		p2p_err("noa info is null");
		return QDF_STATUS_E_INVAL;
	}

	noa_attr = qdf_mem_malloc(sizeof(*noa_attr));
	if (!noa_attr)
		return QDF_STATUS_E_NOMEM;

	noa_attr->index = noa_info->index;
	noa_attr->opps_ps = noa_info->opps_ps;
	noa_attr->ct_win = noa_info->ct_window;
	if (!noa_info->num_desc) {
		p2p_debug("Zero noa descriptors");
	} else {
		p2p_debug("%d noa descriptors", noa_info->num_desc);

		noa_attr->noa1_count =
			noa_info->noa_desc[0].type_count;
		noa_attr->noa1_duration =
			noa_info->noa_desc[0].duration;
		noa_attr->noa1_interval =
			noa_info->noa_desc[0].interval;
		noa_attr->noa1_start_time =
			noa_info->noa_desc[0].start_time;
		if (noa_info->num_desc > 1) {
			noa_attr->noa2_count =
				noa_info->noa_desc[1].type_count;
			noa_attr->noa2_duration =
				noa_info->noa_desc[1].duration;
			noa_attr->noa2_interval =
				noa_info->noa_desc[1].interval;
			noa_attr->noa2_start_time =
				noa_info->noa_desc[1].start_time;
		}
	}

	p2p_debug("Sending P2P_NOA_ATTR_IND to pe");

	msg.type = P2P_NOA_ATTR_IND;
	msg.bodyval = 0;
	msg.bodyptr = noa_attr;
	status = scheduler_post_message(QDF_MODULE_ID_P2P,
					QDF_MODULE_ID_P2P,
					QDF_MODULE_ID_PE,
					&msg);
	if (QDF_IS_STATUS_ERROR(status)) {
		qdf_mem_free(noa_attr);
		p2p_err("post msg fail:%d", status);
	}

	return status;
}

/**
 * process_peer_for_noa() - disable NoA
 * @vdev: vdev object
 * @psoc: soc object
 * @peer: peer object
 *
 * This function disables NoA
 *
 *
 * Return: QDF_STATUS
 */
static QDF_STATUS process_peer_for_noa(struct wlan_objmgr_vdev *vdev,
				struct wlan_objmgr_psoc *psoc,
				struct wlan_objmgr_peer *peer)
{
	struct p2p_vdev_priv_obj *p2p_vdev_obj = NULL;
	enum QDF_OPMODE mode;
	enum wlan_peer_type peer_type;
	bool disable_noa;
	uint8_t vdev_id;

	if (!vdev || !psoc || !peer) {
		p2p_err("vdev:%pK psoc:%pK peer:%pK", vdev, psoc, peer);
		return QDF_STATUS_E_INVAL;
	}
	p2p_vdev_obj = wlan_objmgr_vdev_get_comp_private_obj(vdev,
						WLAN_UMAC_COMP_P2P);
	if (!p2p_vdev_obj)
		return QDF_STATUS_E_INVAL;

	mode = wlan_vdev_mlme_get_opmode(vdev);

	peer_type = wlan_peer_get_peer_type(peer);
	if (peer_type == WLAN_PEER_STA)
		p2p_vdev_obj->non_p2p_peer_count++;

	disable_noa = ((mode == QDF_P2P_GO_MODE)
			&& p2p_vdev_obj->non_p2p_peer_count
			&& p2p_vdev_obj->noa_status);

	if (disable_noa && (peer_type == WLAN_PEER_STA)) {

		vdev_id = wlan_vdev_get_id(vdev);

		if (ucfg_p2p_set_noa(psoc, vdev_id,
				true) == QDF_STATUS_SUCCESS) {
			p2p_vdev_obj->noa_status = false;
		} else {
			p2p_vdev_obj->noa_status = true;
		}
		p2p_debug("NoA status: %d", p2p_vdev_obj->noa_status);
	}
	p2p_debug("process_peer_for_noa");

	return QDF_STATUS_SUCCESS;
}

/**
 * p2p_object_init_params() - init parameters for p2p object
 * @psoc:        pointer to psoc object
 * @p2p_soc_obj: pointer to p2p psoc object
 *
 * This function init parameters for p2p object
 */
static QDF_STATUS p2p_object_init_params(
	struct wlan_objmgr_psoc *psoc,
	struct p2p_soc_priv_obj *p2p_soc_obj)
{
	if (!psoc || !p2p_soc_obj) {
		p2p_err("invalid param");
		return QDF_STATUS_E_INVAL;
	}

	p2p_soc_obj->param.go_keepalive_period =
			cfg_get(psoc, CFG_GO_KEEP_ALIVE_PERIOD);
	p2p_soc_obj->param.go_link_monitor_period =
			cfg_get(psoc, CFG_GO_LINK_MONITOR_PERIOD);
	p2p_soc_obj->param.p2p_device_addr_admin =
			cfg_get(psoc, CFG_P2P_DEVICE_ADDRESS_ADMINISTRATED);
	p2p_soc_obj->param.is_random_seq_num_enabled =
			cfg_get(psoc, CFG_ACTION_FRAME_RANDOM_SEQ_NUM_ENABLED);
	p2p_soc_obj->param.indoor_channel_support =
			cfg_get(psoc, CFG_P2P_GO_ON_5GHZ_INDOOR_CHANNEL);
	return QDF_STATUS_SUCCESS;
}

#ifdef WLAN_FEATURE_P2P_DEBUG
/**
 * wlan_p2p_init_connection_status() - init connection status
 * @p2p_soc_obj: pointer to p2p psoc object
 *
 * This function initial p2p connection status.
 *
 * Return: None
 */
static void wlan_p2p_init_connection_status(
		struct p2p_soc_priv_obj *p2p_soc_obj)
{
	if (!p2p_soc_obj) {
		p2p_err("invalid p2p soc obj");
		return;
	}

	p2p_soc_obj->connection_status = P2P_NOT_ACTIVE;
}
#else
static void wlan_p2p_init_connection_status(
		struct p2p_soc_priv_obj *p2p_soc_obj)
{
}
#endif /* WLAN_FEATURE_P2P_DEBUG */

QDF_STATUS p2p_component_init(void)
{
	QDF_STATUS status;

	status = wlan_objmgr_register_psoc_create_handler(
				WLAN_UMAC_COMP_P2P,
				p2p_psoc_obj_create_notification,
				NULL);
	if (status != QDF_STATUS_SUCCESS) {
		p2p_err("Failed to register p2p obj create handler");
		goto err_reg_psoc_create;
	}

	status = wlan_objmgr_register_psoc_destroy_handler(
				WLAN_UMAC_COMP_P2P,
				p2p_psoc_obj_destroy_notification,
				NULL);
	if (status != QDF_STATUS_SUCCESS) {
		p2p_err("Failed to register p2p obj delete handler");
		goto err_reg_psoc_delete;
	}

	status = wlan_objmgr_register_vdev_create_handler(
				WLAN_UMAC_COMP_P2P,
				p2p_vdev_obj_create_notification,
				NULL);
	if (status != QDF_STATUS_SUCCESS) {
		p2p_err("Failed to register p2p vdev create handler");
		goto err_reg_vdev_create;
	}

	status = wlan_objmgr_register_vdev_destroy_handler(
				WLAN_UMAC_COMP_P2P,
				p2p_vdev_obj_destroy_notification,
				NULL);
	if (status != QDF_STATUS_SUCCESS) {
		p2p_err("Failed to register p2p vdev delete handler");
		goto err_reg_vdev_delete;
	}

	status = wlan_objmgr_register_peer_create_handler(
				WLAN_UMAC_COMP_P2P,
				p2p_peer_obj_create_notification,
				NULL);
	if (status != QDF_STATUS_SUCCESS) {
		p2p_err("Failed to register p2p peer create handler");
		goto err_reg_peer_create;
	}

	status = wlan_objmgr_register_peer_destroy_handler(
				WLAN_UMAC_COMP_P2P,
				p2p_peer_obj_destroy_notification,
				NULL);
	if (status != QDF_STATUS_SUCCESS) {
		p2p_err("Failed to register p2p peer destroy handler");
		goto err_reg_peer_destroy;
	}

	p2p_debug("Register p2p obj handler successful");

	return QDF_STATUS_SUCCESS;
err_reg_peer_destroy:
	wlan_objmgr_unregister_peer_create_handler(WLAN_UMAC_COMP_P2P,
			p2p_peer_obj_create_notification, NULL);
err_reg_peer_create:
	wlan_objmgr_unregister_vdev_destroy_handler(WLAN_UMAC_COMP_P2P,
			p2p_vdev_obj_destroy_notification, NULL);
err_reg_vdev_delete:
	wlan_objmgr_unregister_vdev_create_handler(WLAN_UMAC_COMP_P2P,
			p2p_vdev_obj_create_notification, NULL);
err_reg_vdev_create:
	wlan_objmgr_unregister_psoc_destroy_handler(WLAN_UMAC_COMP_P2P,
			p2p_psoc_obj_destroy_notification, NULL);
err_reg_psoc_delete:
	wlan_objmgr_unregister_psoc_create_handler(WLAN_UMAC_COMP_P2P,
			p2p_psoc_obj_create_notification, NULL);
err_reg_psoc_create:
	return status;
}

QDF_STATUS p2p_component_deinit(void)
{
	QDF_STATUS status;
	QDF_STATUS ret_status = QDF_STATUS_SUCCESS;

	status = wlan_objmgr_unregister_vdev_create_handler(
				WLAN_UMAC_COMP_P2P,
				p2p_vdev_obj_create_notification,
				NULL);
	if (status != QDF_STATUS_SUCCESS) {
		p2p_err("Failed to unregister p2p vdev create handler, %d",
			status);
		ret_status = status;
	}

	status = wlan_objmgr_unregister_vdev_destroy_handler(
				WLAN_UMAC_COMP_P2P,
				p2p_vdev_obj_destroy_notification,
				NULL);
	if (status != QDF_STATUS_SUCCESS) {
		p2p_err("Failed to unregister p2p vdev delete handler, %d",
			status);
		ret_status = status;
	}

	status = wlan_objmgr_unregister_psoc_create_handler(
				WLAN_UMAC_COMP_P2P,
				p2p_psoc_obj_create_notification,
				NULL);
	if (status != QDF_STATUS_SUCCESS) {
		p2p_err("Failed to unregister p2p obj create handler, %d",
			status);
		ret_status = status;
	}

	status = wlan_objmgr_unregister_psoc_destroy_handler(
				WLAN_UMAC_COMP_P2P,
				p2p_psoc_obj_destroy_notification,
				NULL);
	if (status != QDF_STATUS_SUCCESS) {
		p2p_err("Failed to unregister p2p obj delete handler, %d",
			status);
		ret_status = status;
	}

	p2p_debug("Unregister p2p obj handler complete");

	return ret_status;
}

QDF_STATUS p2p_psoc_object_open(struct wlan_objmgr_psoc *soc)
{
	QDF_STATUS status;
	struct p2p_soc_priv_obj *p2p_soc_obj;

	if (!soc) {
		p2p_err("psoc context passed is NULL");
		return QDF_STATUS_E_INVAL;
	}

	p2p_soc_obj = wlan_objmgr_psoc_get_comp_private_obj(soc,
			WLAN_UMAC_COMP_P2P);
	if (!p2p_soc_obj) {
		p2p_err("p2p soc private object is NULL");
		return QDF_STATUS_E_FAILURE;
	}

	p2p_object_init_params(soc, p2p_soc_obj);
	qdf_list_create(&p2p_soc_obj->roc_q, MAX_QUEUE_LENGTH);
	qdf_list_create(&p2p_soc_obj->tx_q_roc, MAX_QUEUE_LENGTH);
	qdf_list_create(&p2p_soc_obj->tx_q_ack, MAX_QUEUE_LENGTH);

	status = qdf_event_create(&p2p_soc_obj->cleanup_roc_done);
	if (status != QDF_STATUS_SUCCESS) {
		p2p_err("failed to create cleanup roc done event");
		goto fail_cleanup_roc;
	}

	status = qdf_event_create(&p2p_soc_obj->cleanup_tx_done);
	if (status != QDF_STATUS_SUCCESS) {
		p2p_err("failed to create cleanup roc done event");
		goto fail_cleanup_tx;
	}

	qdf_runtime_lock_init(&p2p_soc_obj->roc_runtime_lock);
	p2p_soc_obj->cur_roc_vdev_id = P2P_INVALID_VDEV_ID;
	qdf_idr_create(&p2p_soc_obj->p2p_idr);

	p2p_debug("p2p psoc object open successful");

	return QDF_STATUS_SUCCESS;

fail_cleanup_tx:
	qdf_event_destroy(&p2p_soc_obj->cleanup_roc_done);

fail_cleanup_roc:
	qdf_list_destroy(&p2p_soc_obj->tx_q_ack);
	qdf_list_destroy(&p2p_soc_obj->tx_q_roc);
	qdf_list_destroy(&p2p_soc_obj->roc_q);

	return status;
}

QDF_STATUS p2p_psoc_object_close(struct wlan_objmgr_psoc *soc)
{
	struct p2p_soc_priv_obj *p2p_soc_obj;

	if (!soc) {
		p2p_err("psoc context passed is NULL");
		return QDF_STATUS_E_INVAL;
	}

	p2p_soc_obj = wlan_objmgr_psoc_get_comp_private_obj(soc,
			WLAN_UMAC_COMP_P2P);
	if (!p2p_soc_obj) {
		p2p_err("p2p soc object is NULL");
		return QDF_STATUS_E_FAILURE;
	}

	qdf_idr_destroy(&p2p_soc_obj->p2p_idr);
	qdf_runtime_lock_deinit(&p2p_soc_obj->roc_runtime_lock);
	qdf_event_destroy(&p2p_soc_obj->cleanup_tx_done);
	qdf_event_destroy(&p2p_soc_obj->cleanup_roc_done);
	qdf_list_destroy(&p2p_soc_obj->tx_q_ack);
	qdf_list_destroy(&p2p_soc_obj->tx_q_roc);
	qdf_list_destroy(&p2p_soc_obj->roc_q);

	p2p_debug("p2p psoc object close successful");

	return QDF_STATUS_SUCCESS;
}

#ifdef FEATURE_P2P_LISTEN_OFFLOAD
static inline void p2p_init_lo_event(struct p2p_start_param *start_param,
				     struct p2p_start_param *req)
{
	start_param->lo_event_cb = req->lo_event_cb;
	start_param->lo_event_cb_data = req->lo_event_cb_data;
}
#else
static inline void p2p_init_lo_event(struct p2p_start_param *start_param,
				     struct p2p_start_param *req)
{
}
#endif

QDF_STATUS p2p_psoc_start(struct wlan_objmgr_psoc *soc,
	struct p2p_start_param *req)
{
	struct p2p_soc_priv_obj *p2p_soc_obj;
	struct p2p_start_param *start_param;

	if (!soc) {
		p2p_err("psoc context passed is NULL");
		return QDF_STATUS_E_INVAL;
	}

	p2p_soc_obj = wlan_objmgr_psoc_get_comp_private_obj(soc,
			WLAN_UMAC_COMP_P2P);
	if (!p2p_soc_obj) {
		p2p_err("P2P soc object is NULL");
		return QDF_STATUS_E_FAILURE;
	}

	start_param = qdf_mem_malloc(sizeof(*start_param));
	if (!start_param)
		return QDF_STATUS_E_NOMEM;

	start_param->rx_cb = req->rx_cb;
	start_param->rx_cb_data = req->rx_cb_data;
	start_param->event_cb = req->event_cb;
	start_param->event_cb_data = req->event_cb_data;
	start_param->tx_cnf_cb = req->tx_cnf_cb;
	start_param->tx_cnf_cb_data = req->tx_cnf_cb_data;
	p2p_init_lo_event(start_param, req);
	p2p_soc_obj->start_param = start_param;

	wlan_p2p_init_connection_status(p2p_soc_obj);

	/* register p2p lo stop and noa event */
	tgt_p2p_register_lo_ev_handler(soc);
	tgt_p2p_register_noa_ev_handler(soc);
	tgt_p2p_register_macaddr_rx_filter_evt_handler(soc, true);
	tgt_p2p_register_mcc_quota_ev_handler(soc, true);
	/* register scan request id */
	p2p_soc_obj->scan_req_id = wlan_scan_register_requester(
		soc, P2P_MODULE_NAME, tgt_p2p_scan_event_cb,
		p2p_soc_obj);

	/* register rx action frame */
	p2p_mgmt_rx_action_ops(soc, true);

	p2p_debug("p2p psoc start successful, scan request id:%d",
		p2p_soc_obj->scan_req_id);

	return QDF_STATUS_SUCCESS;
}

QDF_STATUS p2p_psoc_stop(struct wlan_objmgr_psoc *soc)
{
	struct p2p_soc_priv_obj *p2p_soc_obj;
	struct p2p_start_param *start_param;

	if (!soc) {
		p2p_err("psoc context passed is NULL");
		return QDF_STATUS_E_INVAL;
	}

	p2p_soc_obj = wlan_objmgr_psoc_get_comp_private_obj(soc,
			WLAN_UMAC_COMP_P2P);
	if (!p2p_soc_obj) {
		p2p_err("P2P soc object is NULL");
		return QDF_STATUS_E_FAILURE;
	}

	start_param = p2p_soc_obj->start_param;
	p2p_soc_obj->start_param = NULL;
	if (!start_param) {
		p2p_err("start parameters is NULL");
		return QDF_STATUS_E_FAILURE;
	}

	/* unregister rx action frame */
	p2p_mgmt_rx_action_ops(soc, false);

	/* clean up queue of p2p psoc private object */
	p2p_cleanup_tx_sync(p2p_soc_obj, NULL);
	p2p_cleanup_roc(p2p_soc_obj, NULL, true);

	/* unrgister scan request id*/
	wlan_scan_unregister_requester(soc, p2p_soc_obj->scan_req_id);

	tgt_p2p_register_mcc_quota_ev_handler(soc, false);
	/* unregister p2p lo stop and noa event */
	tgt_p2p_register_macaddr_rx_filter_evt_handler(soc, false);
	tgt_p2p_unregister_lo_ev_handler(soc);
	tgt_p2p_unregister_noa_ev_handler(soc);

	start_param->rx_cb = NULL;
	start_param->rx_cb_data = NULL;
	start_param->event_cb = NULL;
	start_param->event_cb_data = NULL;
	start_param->tx_cnf_cb = NULL;
	start_param->tx_cnf_cb_data = NULL;
	qdf_mem_free(start_param);

	p2p_debug("p2p psoc stop successful");

	return QDF_STATUS_SUCCESS;
}

QDF_STATUS p2p_process_cmd(struct scheduler_msg *msg)
{
	QDF_STATUS status;

	p2p_debug("msg type %d, %s", msg->type,
		p2p_get_cmd_type_str(msg->type));

	if (!(msg->bodyptr)) {
		p2p_err("Invalid message body");
		return QDF_STATUS_E_INVAL;
	}
	switch (msg->type) {
	case P2P_ROC_REQ:
		status = p2p_process_roc_req(
				(struct p2p_roc_context *)
				msg->bodyptr);
		break;
	case P2P_CANCEL_ROC_REQ:
		status = p2p_process_cancel_roc_req(
				(struct cancel_roc_context *)
				msg->bodyptr);
		qdf_mem_free(msg->bodyptr);
		break;
	case P2P_MGMT_TX:
		status = p2p_process_mgmt_tx(
				(struct tx_action_context *)
				msg->bodyptr);
		break;
	case P2P_MGMT_TX_CANCEL:
		status = p2p_process_mgmt_tx_cancel(
				(struct cancel_roc_context *)
				msg->bodyptr);
		qdf_mem_free(msg->bodyptr);
		break;
	case P2P_CLEANUP_ROC:
		status = p2p_process_cleanup_roc_queue(
				(struct p2p_cleanup_param *)
				msg->bodyptr);
		qdf_mem_free(msg->bodyptr);
		break;
	case P2P_CLEANUP_TX:
		status = p2p_process_cleanup_tx_queue(
				(struct p2p_cleanup_param *)
				msg->bodyptr);
		qdf_mem_free(msg->bodyptr);
		break;
	case P2P_SET_RANDOM_MAC:
		status = p2p_process_set_rand_mac(msg->bodyptr);
		qdf_mem_free(msg->bodyptr);
		break;

	default:
		p2p_err("drop unexpected message received %d",
			msg->type);
		status = QDF_STATUS_E_INVAL;
		break;
	}

	return status;
}

QDF_STATUS p2p_process_evt(struct scheduler_msg *msg)
{
	QDF_STATUS status;

	p2p_debug("msg type %d, %s", msg->type,
		p2p_get_event_type_str(msg->type));

	if (!(msg->bodyptr)) {
		p2p_err("Invalid message body");
		return QDF_STATUS_E_INVAL;
	}

	switch (msg->type) {
	case P2P_EVENT_MGMT_TX_ACK_CNF:
		status = p2p_process_mgmt_tx_ack_cnf(
				(struct p2p_tx_conf_event *)
				msg->bodyptr);
		break;
	case P2P_EVENT_RX_MGMT:
		status  = p2p_process_rx_mgmt(
				(struct p2p_rx_mgmt_event *)
				msg->bodyptr);
		break;
	case P2P_EVENT_LO_STOPPED:
		status = p2p_process_lo_stop(
				(struct p2p_lo_stop_event *)
				msg->bodyptr);
		break;
	case P2P_EVENT_NOA:
		status = p2p_process_noa(
				(struct p2p_noa_event *)
				msg->bodyptr);
		break;
	case P2P_EVENT_ADD_MAC_RSP:
		status = p2p_process_set_rand_mac_rsp(
				(struct p2p_mac_filter_rsp *)
				msg->bodyptr);
		break;
	default:
		p2p_err("Drop unexpected message received %d",
			msg->type);
		status = QDF_STATUS_E_INVAL;
		break;
	}

	qdf_mem_free(msg->bodyptr);
	msg->bodyptr = NULL;

	return status;
}

QDF_STATUS p2p_msg_flush_callback(struct scheduler_msg *msg)
{
	struct tx_action_context *tx_action;

	if (!msg || !(msg->bodyptr)) {
		p2p_err("invalid msg");
		return QDF_STATUS_E_INVAL;
	}

	p2p_debug("flush msg, type:%d", msg->type);
	switch (msg->type) {
	case P2P_MGMT_TX:
		tx_action = (struct tx_action_context *)msg->bodyptr;
		qdf_mem_free(tx_action->buf);
		qdf_mem_free(tx_action);
		break;
	default:
		qdf_mem_free(msg->bodyptr);
		break;
	}

	return QDF_STATUS_SUCCESS;
}

QDF_STATUS p2p_event_flush_callback(struct scheduler_msg *msg)
{
	struct p2p_noa_event *noa_event;
	struct p2p_rx_mgmt_event *rx_mgmt_event;
	struct p2p_tx_conf_event *tx_conf_event;
	struct p2p_lo_stop_event *lo_stop_event;

	if (!msg || !(msg->bodyptr)) {
		p2p_err("invalid msg");
		return QDF_STATUS_E_INVAL;
	}

	p2p_debug("flush event, type:%d", msg->type);
	switch (msg->type) {
	case P2P_EVENT_NOA:
		noa_event = (struct p2p_noa_event *)msg->bodyptr;
		qdf_mem_free(noa_event->noa_info);
		qdf_mem_free(noa_event);
		break;
	case P2P_EVENT_RX_MGMT:
		rx_mgmt_event = (struct p2p_rx_mgmt_event *)msg->bodyptr;
		qdf_mem_free(rx_mgmt_event->rx_mgmt);
		qdf_mem_free(rx_mgmt_event);
		break;
	case P2P_EVENT_MGMT_TX_ACK_CNF:
		tx_conf_event = (struct p2p_tx_conf_event *)msg->bodyptr;
		qdf_nbuf_free(tx_conf_event->nbuf);
		qdf_mem_free(tx_conf_event);
		break;
	case P2P_EVENT_LO_STOPPED:
		lo_stop_event = (struct p2p_lo_stop_event *)msg->bodyptr;
		qdf_mem_free(lo_stop_event->lo_event);
		qdf_mem_free(lo_stop_event);
		break;
	default:
		qdf_mem_free(msg->bodyptr);
		break;
	}

	return QDF_STATUS_SUCCESS;
}

bool p2p_check_oui_and_force_1x1(uint8_t *assoc_ie, uint32_t assoc_ie_len)
{
	const uint8_t *vendor_ie, *p2p_ie, *pos;
	uint8_t rem_len, attr;
	uint16_t attr_len;

	vendor_ie = (uint8_t *)p2p_get_p2pie_ptr(assoc_ie, assoc_ie_len);
	if (!vendor_ie) {
		p2p_debug("P2P IE not found");
		return false;
	}

	rem_len = vendor_ie[1];
	if (rem_len < (2 + OUI_SIZE_P2P) || rem_len > WLAN_MAX_IE_LEN) {
		p2p_err("Invalid IE len %d", rem_len);
		return false;
	}

	p2p_ie = vendor_ie + HEADER_LEN_P2P_IE;
	rem_len -= OUI_SIZE_P2P;

	while (rem_len) {
		attr = p2p_ie[0];
		attr_len = LE_READ_2(&p2p_ie[1]);
		if (attr_len > rem_len)  {
			p2p_err("Invalid len %d for elem:%d", attr_len, attr);
			return false;
		}

		switch (attr) {
		case P2P_ATTR_CAPABILITY:
		case P2P_ATTR_DEVICE_ID:
		case P2P_ATTR_GROUP_OWNER_INTENT:
		case P2P_ATTR_STATUS:
		case P2P_ATTR_LISTEN_CHANNEL:
		case P2P_ATTR_OPERATING_CHANNEL:
		case P2P_ATTR_GROUP_INFO:
		case P2P_ATTR_MANAGEABILITY:
		case P2P_ATTR_CHANNEL_LIST:
			break;

		case P2P_ATTR_DEVICE_INFO:
			if (attr_len < (QDF_MAC_ADDR_SIZE +
					MAX_CONFIG_METHODS_LEN + 8 +
					DEVICE_CATEGORY_MAX_LEN)) {
				p2p_err("Invalid Device info attr len %d",
					attr_len);
				return false;
			}

			/* move by attr id and 2 bytes of attr len */
			pos = p2p_ie + 3;

			/*
			 * the P2P Device info is of format:
			 * attr_id - 1 byte
			 * attr_len - 2 bytes
			 * device mac addr - 6 bytes
			 * config methods - 2 bytes
			 * primary device type - 8bytes
			 *  -primary device type category - 1 byte
			 *  -primary device type oui - 4bytes
			 * number of secondary device type - 2 bytes
			 */
			pos += ETH_ALEN + MAX_CONFIG_METHODS_LEN +
			       DEVICE_CATEGORY_MAX_LEN;

			if (!qdf_mem_cmp(pos, P2P_1X1_WAR_OUI,
					 P2P_1X1_OUI_LEN))
				return true;

			break;
		default:
			p2p_err("Invalid P2P attribute");
			break;
		}
		p2p_ie += (3 + attr_len);
		rem_len -= (3 + attr_len);
	}

	return false;
}

#ifdef FEATURE_P2P_LISTEN_OFFLOAD
QDF_STATUS p2p_process_lo_stop(
	struct p2p_lo_stop_event *lo_stop_event)
{
	struct p2p_lo_event *lo_evt;
	struct p2p_soc_priv_obj *p2p_soc_obj;
	struct p2p_start_param *start_param;

	if (!lo_stop_event) {
		p2p_err("invalid lo stop event");
		return QDF_STATUS_E_INVAL;
	}

	lo_evt = lo_stop_event->lo_event;
	if (!lo_evt) {
		p2p_err("invalid lo event");
		return QDF_STATUS_E_INVAL;
	}

	p2p_soc_obj = lo_stop_event->p2p_soc_obj;

	p2p_debug("vdev_id %d, reason %d",
		lo_evt->vdev_id, lo_evt->reason_code);

	if (!p2p_soc_obj || !(p2p_soc_obj->start_param)) {
		p2p_err("Invalid p2p soc object or start parameters");
		qdf_mem_free(lo_evt);
		return QDF_STATUS_E_INVAL;
	}
	start_param = p2p_soc_obj->start_param;
	if (start_param->lo_event_cb)
		start_param->lo_event_cb(
			start_param->lo_event_cb_data, lo_evt);
	else
		p2p_err("Invalid p2p soc obj or hdd lo event callback");

	qdf_mem_free(lo_evt);

	return QDF_STATUS_SUCCESS;
}
#endif

QDF_STATUS p2p_process_noa(struct p2p_noa_event *noa_event)
{
	QDF_STATUS status = QDF_STATUS_SUCCESS;
	struct p2p_noa_info *noa_info;
	struct p2p_vdev_priv_obj *p2p_vdev_obj;
	struct p2p_soc_priv_obj *p2p_soc_obj;
	struct wlan_objmgr_vdev *vdev;
	struct wlan_objmgr_psoc *psoc;
	enum QDF_OPMODE mode;

	if (!noa_event) {
		p2p_err("invalid noa event");
		return QDF_STATUS_E_INVAL;
	}
	noa_info = noa_event->noa_info;
	p2p_soc_obj = noa_event->p2p_soc_obj;
	psoc = p2p_soc_obj->soc;

	p2p_debug("psoc:%pK, index:%d, opps_ps:%d, ct_window:%d, num_desc:%d, vdev_id:%d",
		psoc, noa_info->index, noa_info->opps_ps,
		noa_info->ct_window, noa_info->num_desc,
		noa_info->vdev_id);

	vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc,
			noa_info->vdev_id, WLAN_P2P_ID);
	if (!vdev) {
		p2p_err("vdev obj is NULL");
		qdf_mem_free(noa_event->noa_info);
		return QDF_STATUS_E_INVAL;
	}

	mode = wlan_vdev_mlme_get_opmode(vdev);
	p2p_debug("vdev mode:%d", mode);
	if (mode != QDF_P2P_GO_MODE) {
		p2p_err("invalid p2p vdev mode:%d", mode);
		status = QDF_STATUS_E_INVAL;
		goto fail;
	}

	/* must send noa to pe since of limitation*/
	p2p_send_noa_to_pe(noa_info);

	p2p_vdev_obj = wlan_objmgr_vdev_get_comp_private_obj(vdev,
			WLAN_UMAC_COMP_P2P);
	if (!(p2p_vdev_obj->noa_info)) {
		p2p_vdev_obj->noa_info =
			qdf_mem_malloc(sizeof(struct p2p_noa_info));
		if (!(p2p_vdev_obj->noa_info)) {
			status = QDF_STATUS_E_NOMEM;
			goto fail;
		}
	}
	qdf_mem_copy(p2p_vdev_obj->noa_info, noa_info,
		sizeof(struct p2p_noa_info));
fail:
	qdf_mem_free(noa_event->noa_info);
	wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID);

	return status;
}

void p2p_peer_authorized(struct wlan_objmgr_vdev *vdev, uint8_t *mac_addr)
{
	QDF_STATUS status;
	struct wlan_objmgr_psoc *psoc;
	struct wlan_objmgr_peer *peer;
	uint8_t pdev_id;

	if (!vdev) {
		p2p_err("vdev:%pK", vdev);
		return;
	}
	psoc = wlan_vdev_get_psoc(vdev);
	if (!psoc) {
		p2p_err("psoc:%pK", psoc);
		return;
	}
	pdev_id = wlan_objmgr_pdev_get_pdev_id(wlan_vdev_get_pdev(vdev));
	peer = wlan_objmgr_get_peer(psoc, pdev_id,  mac_addr, WLAN_P2P_ID);
	if (!peer) {
		p2p_debug("peer info not found");
		return;
	}
	status = process_peer_for_noa(vdev, psoc, peer);
	wlan_objmgr_peer_release_ref(peer, WLAN_P2P_ID);

	if (status != QDF_STATUS_SUCCESS)
		return;

	p2p_debug("peer is authorized");
}

#ifdef WLAN_FEATURE_P2P_DEBUG

void p2p_status_update(struct p2p_soc_priv_obj *p2p_soc_obj,
		       enum p2p_connection_status status)
{
	 p2p_soc_obj->connection_status = status;
}

static struct p2p_soc_priv_obj *
get_p2p_soc_obj_by_vdev(struct wlan_objmgr_vdev *vdev)
{
	struct p2p_soc_priv_obj *p2p_soc_obj;
	struct wlan_objmgr_psoc *soc;

	if (!vdev) {
		p2p_err("vdev context passed is NULL");
		return NULL;
	}

	soc = wlan_vdev_get_psoc(vdev);
	if (!soc) {
		p2p_err("soc context is NULL");
		return NULL;
	}

	p2p_soc_obj = wlan_objmgr_psoc_get_comp_private_obj(soc,
			WLAN_UMAC_COMP_P2P);
	if (!p2p_soc_obj)
		p2p_err("P2P soc context is NULL");

	return p2p_soc_obj;
}

QDF_STATUS p2p_status_scan(struct wlan_objmgr_vdev *vdev)
{
	struct p2p_soc_priv_obj *p2p_soc_obj;
	enum QDF_OPMODE mode;

	p2p_soc_obj = get_p2p_soc_obj_by_vdev(vdev);
	if (!p2p_soc_obj) {
		p2p_err("P2P soc context is NULL");
		return QDF_STATUS_E_FAILURE;
	}

	mode = wlan_vdev_mlme_get_opmode(vdev);
	if (mode != QDF_P2P_CLIENT_MODE &&
	    mode != QDF_P2P_DEVICE_MODE) {
		p2p_debug("this is not P2P CLIENT or DEVICE, mode:%d",
			mode);
		return QDF_STATUS_SUCCESS;
	}

	p2p_debug("connection status:%d", p2p_soc_obj->connection_status);
	switch (p2p_soc_obj->connection_status) {
	case P2P_GO_NEG_COMPLETED:
	case P2P_GO_NEG_PROCESS:
		p2p_status_update(p2p_soc_obj,
				  P2P_CLIENT_CONNECTING_STATE_1);
		p2p_debug("[P2P State] Changing state from Go nego completed to Connection is started");
		p2p_debug("P2P Scanning is started for 8way Handshake");
		break;
	case P2P_CLIENT_DISCONNECTED_STATE:
		p2p_status_update(p2p_soc_obj,
				  P2P_CLIENT_CONNECTING_STATE_2);
		p2p_debug("[P2P State] Changing state from Disconnected state to Connection is started");
		p2p_debug("P2P Scanning is started for 4way Handshake");
		break;
	default:
		break;
	}

	return QDF_STATUS_SUCCESS;
}

QDF_STATUS p2p_status_connect(struct wlan_objmgr_vdev *vdev)
{
	struct p2p_soc_priv_obj *p2p_soc_obj;
	enum QDF_OPMODE mode;

	p2p_soc_obj = get_p2p_soc_obj_by_vdev(vdev);
	if (!p2p_soc_obj) {
		p2p_err("P2P soc context is NULL");
		return QDF_STATUS_E_FAILURE;
	}

	mode = wlan_vdev_mlme_get_opmode(vdev);
	if (mode != QDF_P2P_CLIENT_MODE)
		return QDF_STATUS_SUCCESS;

	p2p_debug("connection status:%d", p2p_soc_obj->connection_status);
	switch (p2p_soc_obj->connection_status) {
	case P2P_CLIENT_CONNECTING_STATE_1:
		p2p_status_update(p2p_soc_obj,
				  P2P_CLIENT_CONNECTED_STATE_1);
		p2p_debug("[P2P State] Changing state from Connecting state to Connected State for 8-way Handshake");
		break;
	case P2P_CLIENT_DISCONNECTED_STATE:
		p2p_debug("No scan before 4-way handshake");
		/*
		 * since no scan before 4-way handshake and
		 * won't enter state P2P_CLIENT_CONNECTING_STATE_2:
		 */
		fallthrough;
	case P2P_CLIENT_CONNECTING_STATE_2:
		p2p_status_update(p2p_soc_obj,
				  P2P_CLIENT_COMPLETED_STATE);
		p2p_debug("[P2P State] Changing state from Connecting state to P2P Client Connection Completed");
		break;
	default:
		break;
	}

	return QDF_STATUS_SUCCESS;
}

QDF_STATUS p2p_status_disconnect(struct wlan_objmgr_vdev *vdev)
{
	struct p2p_soc_priv_obj *p2p_soc_obj;
	enum QDF_OPMODE mode;

	p2p_soc_obj = get_p2p_soc_obj_by_vdev(vdev);
	if (!p2p_soc_obj) {
		p2p_err("P2P soc context is NULL");
		return QDF_STATUS_E_FAILURE;
	}

	mode = wlan_vdev_mlme_get_opmode(vdev);
	if (mode != QDF_P2P_CLIENT_MODE)
		return QDF_STATUS_SUCCESS;

	p2p_debug("connection status:%d", p2p_soc_obj->connection_status);
	switch (p2p_soc_obj->connection_status) {
	case P2P_CLIENT_CONNECTED_STATE_1:
		p2p_status_update(p2p_soc_obj,
				  P2P_CLIENT_DISCONNECTED_STATE);
		p2p_debug("[P2P State] 8 way Handshake completed and moved to disconnected state");
		break;
	case P2P_CLIENT_COMPLETED_STATE:
		p2p_status_update(p2p_soc_obj, P2P_NOT_ACTIVE);
		p2p_debug("[P2P State] P2P Client is removed and moved to inactive state");
		break;
	default:
		break;
	}

	return QDF_STATUS_SUCCESS;
}

QDF_STATUS p2p_status_start_bss(struct wlan_objmgr_vdev *vdev)
{
	struct p2p_soc_priv_obj *p2p_soc_obj;
	enum QDF_OPMODE mode;

	p2p_soc_obj = get_p2p_soc_obj_by_vdev(vdev);
	if (!p2p_soc_obj) {
		p2p_err("P2P soc context is NULL");
		return QDF_STATUS_E_FAILURE;
	}

	mode = wlan_vdev_mlme_get_opmode(vdev);
	if (mode != QDF_P2P_GO_MODE) {
		p2p_debug("this is not P2P GO, mode:%d", mode);
		return QDF_STATUS_SUCCESS;
	}

	p2p_debug("connection status:%d", p2p_soc_obj->connection_status);
	switch (p2p_soc_obj->connection_status) {
	case P2P_GO_NEG_COMPLETED:
		p2p_status_update(p2p_soc_obj,
				  P2P_GO_COMPLETED_STATE);
		p2p_debug("[P2P State] From Go nego completed to Non-autonomous Group started");
		break;
	case P2P_NOT_ACTIVE:
		p2p_status_update(p2p_soc_obj,
				  P2P_GO_COMPLETED_STATE);
		p2p_debug("[P2P State] From Inactive to Autonomous Group started");
		break;
	default:
		break;
	}

	return QDF_STATUS_SUCCESS;
}

QDF_STATUS p2p_status_stop_bss(struct wlan_objmgr_vdev *vdev)
{
	struct p2p_soc_priv_obj *p2p_soc_obj;
	enum QDF_OPMODE mode;

	p2p_soc_obj = get_p2p_soc_obj_by_vdev(vdev);
	if (!p2p_soc_obj) {
		p2p_err("P2P soc context is NULL");
		return QDF_STATUS_E_FAILURE;
	}

	mode = wlan_vdev_mlme_get_opmode(vdev);
	if (mode != QDF_P2P_GO_MODE) {
		p2p_debug("this is not P2P GO, mode:%d", mode);
		return QDF_STATUS_SUCCESS;
	}

	p2p_debug("connection status:%d", p2p_soc_obj->connection_status);
	if (p2p_soc_obj->connection_status == P2P_GO_COMPLETED_STATE) {
		p2p_status_update(p2p_soc_obj, P2P_NOT_ACTIVE);
		p2p_debug("[P2P State] From GO completed to Inactive state GO got removed");
	}

	return QDF_STATUS_SUCCESS;
}

#endif /* WLAN_FEATURE_P2P_DEBUG */

#ifdef WLAN_FEATURE_P2P_P2P_STA
QDF_STATUS p2p_check_and_force_scc_go_plus_go(struct wlan_objmgr_psoc *psoc,
					      struct wlan_objmgr_vdev *vdev)
{
	int go_count = 0;
	uint32_t existing_chan_freq, chan_freq;
	enum phy_ch_width existing_ch_width, ch_width;
	uint8_t existing_vdev_id = WLAN_UMAC_VDEV_ID_MAX, vdev_id;
	enum policy_mgr_con_mode existing_vdev_mode = PM_MAX_NUM_OF_MODE;
	bool go_force_scc = false;

	go_count = policy_mgr_mode_specific_connection_count(
			psoc, PM_P2P_GO_MODE, NULL);
	go_force_scc = policy_mgr_go_scc_enforced(psoc);
	if (go_count > 1 && go_force_scc) {
		vdev_id = wlan_vdev_get_id(vdev);
		chan_freq = wlan_get_operation_chan_freq(vdev);
		ch_width = vdev->vdev_mlme.bss_chan->ch_width;
		p2p_debug("Current vdev_id %d, chan_freq %d and ch_width %d",
			  vdev_id, chan_freq, ch_width);
		existing_vdev_id = policy_mgr_fetch_existing_con_info(
				psoc, vdev_id,
				chan_freq,
				&existing_vdev_mode,
				&existing_chan_freq,
				&existing_ch_width);
		p2p_debug("Existing vdev_id %d, chan_freq %d and ch_width %d",
			  existing_vdev_id, existing_chan_freq,
			  existing_ch_width);
		if (existing_vdev_id == WLAN_UMAC_VDEV_ID_MAX) {
			p2p_debug("force scc not required");
			return QDF_STATUS_SUCCESS;
		}
		if (existing_vdev_mode == PM_P2P_GO_MODE) {
			policy_mgr_process_forcescc_for_go(psoc,
							   existing_vdev_id,
							   chan_freq,
							   ch_width,
							   PM_P2P_GO_MODE);
			p2p_debug("CSA for vdev_id %d", existing_vdev_id);
		}
	}
	return QDF_STATUS_SUCCESS;
}
#endif
