diff --git a/p2p/core/src/wlan_p2p_main.c b/p2p/core/src/wlan_p2p_main.c new file mode 100644 index 0000000000..711542da59 --- /dev/null +++ b/p2p/core/src/wlan_p2p_main.c @@ -0,0 +1,1460 @@ +/* + * Copyright (c) 2017-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. + */ + +/** + * DOC: This file contains main P2P function definitions + */ + +#include +#include +#include +#include +#include +#include +#include +#include "wlan_p2p_public_struct.h" +#include "wlan_p2p_ucfg_api.h" +#include "wlan_p2p_tgt_api.h" +#include "wlan_p2p_main.h" +#include "wlan_p2p_roc.h" +#include "wlan_p2p_off_chan_tx.h" +#include "wlan_p2p_cfg.h" +#include "cfg_ucfg_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) { + p2p_err("Failed to allocate p2p soc private object"); + 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) { + p2p_err("Failed to allocate p2p vdev object"); + 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); + + 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; + struct p2p_vdev_priv_obj *p2p_vdev_obj; + enum QDF_OPMODE mode; + enum wlan_peer_type peer_type; + + 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); + peer_type = wlan_peer_get_peer_type(peer); + if ((peer_type == WLAN_PEER_STA) && p2p_vdev_obj) { + + mode = wlan_vdev_mlme_get_opmode(vdev); + if (mode == QDF_P2P_GO_MODE) { + p2p_vdev_obj->non_p2p_peer_count++; + p2p_debug("Non P2P peer count: %d", + p2p_vdev_obj->non_p2p_peer_count); + } + } + 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) { + p2p_err("Failed to allocate memory for tSirP2PNoaAttr"); + 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) { + p2p_err("p2p_vdev_obj:%pK", p2p_vdev_obj); + return QDF_STATUS_E_INVAL; + } + mode = wlan_vdev_mlme_get_opmode(vdev); + + peer_type = wlan_peer_get_peer_type(peer); + + 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.skip_dfs_channel_p2p_search = + cfg_get(psoc, CFG_ENABLE_SKIP_DFS_IN_P2P_SEARCH); + + 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 priviate 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->cancel_roc_done); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("failed to create cancel roc done event"); + goto fail_cancel_roc; + } + + 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_event_destroy(&p2p_soc_obj->cancel_roc_done); + +fail_cancel_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_event_destroy(&p2p_soc_obj->cancel_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) { + p2p_err("Failed to allocate start params"); + 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); + + /* register scan request id */ + p2p_soc_obj->scan_req_id = ucfg_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_sync(p2p_soc_obj, NULL); + + /* unrgister scan request id*/ + ucfg_scan_unregister_requester(soc, p2p_soc_obj->scan_req_id); + + /* 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_mem_free(tx_conf_event); + qdf_nbuf_free(tx_conf_event->nbuf); + 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; +} + +#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)) { + p2p_err("Failed to allocate p2p 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) { + p2p_err("status:%u", status); + return; + } + p2p_debug("peer is authorized"); +} + +#ifdef WLAN_FEATURE_P2P_DEBUG +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_soc_obj->connection_status = + 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_soc_obj->connection_status = + 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) { + p2p_debug("this is not P2P CLIENT, 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_CLIENT_CONNECTING_STATE_1: + p2p_soc_obj->connection_status = + 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"); + /* + * Fall thru since no scan before 4-way handshake and + * won't enter state P2P_CLIENT_CONNECTING_STATE_2: + */ + case P2P_CLIENT_CONNECTING_STATE_2: + p2p_soc_obj->connection_status = + 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) { + p2p_debug("this is not P2P CLIENT, 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_CLIENT_CONNECTED_STATE_1: + p2p_soc_obj->connection_status = + P2P_CLIENT_DISCONNECTED_STATE; + p2p_debug("[P2P State] 8 way Handshake completed and moved to disconnected state"); + break; + case P2P_CLIENT_COMPLETED_STATE: + p2p_soc_obj->connection_status = 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_soc_obj->connection_status = + P2P_GO_COMPLETED_STATE; + p2p_debug("[P2P State] From Go nego completed to Non-autonomous Group started"); + break; + case P2P_NOT_ACTIVE: + p2p_soc_obj->connection_status = + 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_soc_obj->connection_status = 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 */ diff --git a/p2p/core/src/wlan_p2p_main.h b/p2p/core/src/wlan_p2p_main.h new file mode 100644 index 0000000000..6a6632c44e --- /dev/null +++ b/p2p/core/src/wlan_p2p_main.h @@ -0,0 +1,574 @@ +/* + * Copyright (c) 2017-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. + */ + +/** + * DOC: Defines main P2P functions & structures + */ + +#ifndef _WLAN_P2P_MAIN_H_ +#define _WLAN_P2P_MAIN_H_ + +#include +#include +#include +#include +#include +#include + +#define MAX_QUEUE_LENGTH 20 +#define P2P_NOA_ATTR_IND 0x1090 +#define P2P_MODULE_NAME "P2P" +#define P2P_INVALID_VDEV_ID 0xFFFFFFFF +#define MAX_RANDOM_MAC_ADDRS 4 + +#define p2p_debug(params ...) \ + QDF_TRACE_DEBUG(QDF_MODULE_ID_P2P, params) +#define p2p_info(params ...) \ + QDF_TRACE_INFO(QDF_MODULE_ID_P2P, params) +#define p2p_warn(params ...) \ + QDF_TRACE_WARN(QDF_MODULE_ID_P2P, params) +#define p2p_err(params ...) \ + QDF_TRACE_ERROR(QDF_MODULE_ID_P2P, params) +#define p2p_alert(params ...) \ + QDF_TRACE_FATAL(QDF_MODULE_ID_P2P, params) + +#define p2p_nofl_debug(params ...) \ + QDF_TRACE_DEBUG_NO_FL(QDF_MODULE_ID_P2P, params) +#define p2p_nofl_info(params ...) \ + QDF_TRACE_INFO_NO_FL(QDF_MODULE_ID_P2P, params) +#define p2p_nofl_warn(params ...) \ + QDF_TRACE_WARN_NO_FL(QDF_MODULE_ID_P2P, params) +#define p2p_nofl_err(params ...) \ + QDF_TRACE_ERROR_NO_FL(QDF_MODULE_ID_P2P, params) +#define p2p_nofl_alert(params ...) \ + QDF_TRACE_FATAL_NO_FL(QDF_MODULE_ID_P2P, params) + +struct scheduler_msg; +struct p2p_tx_cnf; +struct p2p_rx_mgmt_frame; +struct p2p_lo_event; +struct p2p_start_param; +struct p2p_noa_info; +struct tx_action_context; + +/** + * enum p2p_cmd_type - P2P request type + * @P2P_ROC_REQ: P2P roc request + * @P2P_CANCEL_ROC_REQ: Cancel P2P roc request + * @P2P_MGMT_TX: P2P tx action frame request + * @P2P_MGMT_TX_CANCEL: Cancel tx action frame request + * @P2P_CLEANUP_ROC: Cleanup roc queue + * @P2P_CLEANUP_TX: Cleanup tx mgmt queue + * @P2P_SET_RANDOM_MAC: Set Random MAC addr filter request + */ +enum p2p_cmd_type { + P2P_ROC_REQ = 0, + P2P_CANCEL_ROC_REQ, + P2P_MGMT_TX, + P2P_MGMT_TX_CANCEL, + P2P_CLEANUP_ROC, + P2P_CLEANUP_TX, + P2P_SET_RANDOM_MAC, +}; + +/** + * enum p2p_event_type - P2P event type + * @P2P_EVENT_SCAN_EVENT: P2P scan event + * @P2P_EVENT_MGMT_TX_ACK_CNF: P2P mgmt tx confirm frame + * @P2P_EVENT_RX_MGMT: P2P rx mgmt frame + * @P2P_EVENT_LO_STOPPED: P2P listen offload stopped event + * @P2P_EVENT_NOA: P2P noa event + * @P2P_EVENT_ADD_MAC_RSP: Set Random MAC addr event + */ +enum p2p_event_type { + P2P_EVENT_SCAN_EVENT = 0, + P2P_EVENT_MGMT_TX_ACK_CNF, + P2P_EVENT_RX_MGMT, + P2P_EVENT_LO_STOPPED, + P2P_EVENT_NOA, + P2P_EVENT_ADD_MAC_RSP, +}; + +/** + * struct p2p_tx_conf_event - p2p tx confirm event + * @p2p_soc_obj: p2p soc private object + * @buf: buffer address + * @status: tx status + */ +struct p2p_tx_conf_event { + struct p2p_soc_priv_obj *p2p_soc_obj; + qdf_nbuf_t nbuf; + uint32_t status; +}; + +/** + * struct p2p_rx_mgmt_event - p2p rx mgmt frame event + * @p2p_soc_obj: p2p soc private object + * @rx_mgmt: p2p rx mgmt frame structure + */ +struct p2p_rx_mgmt_event { + struct p2p_soc_priv_obj *p2p_soc_obj; + struct p2p_rx_mgmt_frame *rx_mgmt; +}; + +/** + * struct p2p_lo_stop_event - p2p listen offload stop event + * @p2p_soc_obj: p2p soc private object + * @lo_event: p2p lo stop structure + */ +struct p2p_lo_stop_event { + struct p2p_soc_priv_obj *p2p_soc_obj; + struct p2p_lo_event *lo_event; +}; + +/** + * struct p2p_noa_event - p2p noa event + * @p2p_soc_obj: p2p soc private object + * @noa_info: p2p noa information structure + */ +struct p2p_noa_event { + struct p2p_soc_priv_obj *p2p_soc_obj; + struct p2p_noa_info *noa_info; +}; + +/** + * struct p2p_mac_filter_rsp - p2p set mac filter respone + * @p2p_soc_obj: p2p soc private object + * @vdev_id: vdev id + * @status: successfully(1) or not (0) + */ +struct p2p_mac_filter_rsp { + struct p2p_soc_priv_obj *p2p_soc_obj; + uint32_t vdev_id; + uint32_t status; +}; + +#ifdef WLAN_FEATURE_P2P_DEBUG +/** + * enum p2p_connection_status - p2p connection status + * @P2P_NOT_ACTIVE: P2P not active status + * @P2P_GO_NEG_PROCESS: P2P GO negotiation in process + * @P2P_GO_NEG_COMPLETED: P2P GO negotiation complete + * @P2P_CLIENT_CONNECTING_STATE_1: P2P client connecting state 1 + * @P2P_GO_COMPLETED_STATE: P2P GO complete state + * @P2P_CLIENT_CONNECTED_STATE_1: P2P client connected state 1 + * @P2P_CLIENT_DISCONNECTED_STATE: P2P client disconnected state + * @P2P_CLIENT_CONNECTING_STATE_2: P2P client connecting state 2 + * @P2P_CLIENT_COMPLETED_STATE: P2P client complete state + */ +enum p2p_connection_status { + P2P_NOT_ACTIVE, + P2P_GO_NEG_PROCESS, + P2P_GO_NEG_COMPLETED, + P2P_CLIENT_CONNECTING_STATE_1, + P2P_GO_COMPLETED_STATE, + P2P_CLIENT_CONNECTED_STATE_1, + P2P_CLIENT_DISCONNECTED_STATE, + P2P_CLIENT_CONNECTING_STATE_2, + P2P_CLIENT_COMPLETED_STATE +}; +#endif + +/** + * struct p2p_param - p2p parameters to be used + * @go_keepalive_period: P2P GO keep alive period + * @go_link_monitor_period: period where link is idle and + * where we send NULL frame + * @p2p_device_addr_admin: enable/disable to derive the P2P + * MAC address from the primary MAC address + * @skip_dfs_channel_p2p_search: kip DFS Channel in case of P2P Search + * @ignore_dynamic_dtim_in_p2p_mode:Ignore Dynamic Dtim in case of P2P options + */ +struct p2p_param { + uint32_t go_keepalive_period; + uint32_t go_link_monitor_period; + bool p2p_device_addr_admin; + bool skip_dfs_channel_p2p_search; + bool ignore_dynamic_dtim_in_p2p_mode; +}; + +/** + * struct p2p_soc_priv_obj - Per SoC p2p private object + * @soc: Pointer to SoC context + * @roc_q: Queue for pending roc requests + * @tx_q_roc: Queue for tx frames waiting for RoC + * @tx_q_ack: Queue for tx frames waiting for ack + * @scan_req_id: Scan requestor id + * @start_param: Start parameters, include callbacks and user + * data to HDD + * @cancel_roc_done: Cancel roc done event + * @cleanup_roc_done: Cleanup roc done event + * @cleanup_tx_done: Cleanup tx done event + * @roc_runtime_lock: Runtime lock for roc request + * @p2p_cb: Callbacks to protocol stack + * @cur_roc_vdev_id: Vdev id of current roc + * @p2p_idr: p2p idr + * @param: p2p parameters to be used + * @connection_status:Global P2P connection status + */ +struct p2p_soc_priv_obj { + struct wlan_objmgr_psoc *soc; + qdf_list_t roc_q; + qdf_list_t tx_q_roc; + qdf_list_t tx_q_ack; + wlan_scan_requester scan_req_id; + struct p2p_start_param *start_param; + qdf_event_t cancel_roc_done; + qdf_event_t cleanup_roc_done; + qdf_event_t cleanup_tx_done; + qdf_runtime_lock_t roc_runtime_lock; + struct p2p_protocol_callbacks p2p_cb; + uint32_t cur_roc_vdev_id; + qdf_idr p2p_idr; + struct p2p_param param; +#ifdef WLAN_FEATURE_P2P_DEBUG + enum p2p_connection_status connection_status; +#endif +}; + +/** + * struct action_frame_cookie - Action frame cookie item in cookie list + * @cookie_node: qdf_list_node + * @cookie: Cookie value + */ +struct action_frame_cookie { + qdf_list_node_t cookie_node; + uint64_t cookie; +}; + +/** + * struct action_frame_random_mac - Action Frame random mac addr & + * related attrs + * @p2p_vdev_obj: p2p vdev private obj ptr + * @in_use: Checks whether random mac is in use + * @addr: Contains random mac addr + * @freq: Channel frequency + * @clear_timer: timer to clear random mac filter + * @cookie_list: List of cookies tied with random mac + */ +struct action_frame_random_mac { + struct p2p_vdev_priv_obj *p2p_vdev_obj; + bool in_use; + uint8_t addr[QDF_MAC_ADDR_SIZE]; + uint32_t freq; + qdf_mc_timer_t clear_timer; + qdf_list_t cookie_list; +}; + +/** + * p2p_request_mgr_callback_t() - callback to process set mac filter result + * @result: bool + * @context: callback context. + * + * Return: void + */ +typedef void (*p2p_request_mgr_callback_t)(bool result, void *context); + +/** + * struct random_mac_priv - request private data struct + * @result: result of request. + */ +struct random_mac_priv { + bool result; +}; + +/** + * struct p2p_set_mac_filter_req - set mac addr filter cmd data structure + * @soc: soc object + * @vdev_id: vdev id + * @mac: mac address to be set + * @freq: frequency + * @set: set or clear + * @cb: callback func to be called when the request completion + * @req_cookie: cookie to be used when request completed + */ +struct p2p_set_mac_filter_req { + struct wlan_objmgr_psoc *soc; + uint32_t vdev_id; + uint8_t mac[QDF_MAC_ADDR_SIZE]; + uint32_t freq; + bool set; + p2p_request_mgr_callback_t cb; + void *req_cookie; +}; + +/** + * struct p2p_vdev_priv_obj - Per vdev p2p private object + * @vdev: Pointer to vdev context + * @noa_info: NoA information + * @noa_status: NoA status i.e. Enabled / Disabled (TRUE/FALSE) + * @non_p2p_peer_count: Number of legacy stations connected to this GO + * @random_mac_lock: lock for random_mac list + * @random_mac: active random mac filter lists + * @pending_req: pending set mac filter request. + */ +struct p2p_vdev_priv_obj { + struct wlan_objmgr_vdev *vdev; + struct p2p_noa_info *noa_info; + bool noa_status; + uint16_t non_p2p_peer_count; + + /* random address management for management action frames */ + qdf_spinlock_t random_mac_lock; + struct action_frame_random_mac random_mac[MAX_RANDOM_MAC_ADDRS]; + struct p2p_set_mac_filter_req pending_req; +}; + +/** + * struct p2p_noa_attr - p2p noa attribute + * @rsvd1: reserved bits 1 + * @opps_ps: opps ps state of the AP + * @ct_win: ct window in TUs + * @index: identifies instance of NOA su element + * @rsvd2: reserved bits 2 + * @noa1_count: interval count of noa1 + * @noa1_duration: absent period duration of noa1 + * @noa1_interval: absent period interval of noa1 + * @noa1_start_time: 32 bit tsf time of noa1 + * @rsvd3: reserved bits 3 + * @noa2_count: interval count of noa2 + * @noa2_duration: absent period duration of noa2 + * @noa2_interval: absent period interval of noa2 + * @noa2_start_time: 32 bit tsf time of noa2 + */ +struct p2p_noa_attr { + uint32_t rsvd1:16; + uint32_t ct_win:7; + uint32_t opps_ps:1; + uint32_t index:8; + uint32_t rsvd2:24; + uint32_t noa1_count:8; + uint32_t noa1_duration; + uint32_t noa1_interval; + uint32_t noa1_start_time; + uint32_t rsvd3:24; + uint32_t noa2_count:8; + uint32_t noa2_duration; + uint32_t noa2_interval; + uint32_t noa2_start_time; +}; + +/** + * p2p_component_init() - P2P component initialization + * + * This function registers psoc/vdev create/delete handler. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_component_init(void); + +/** + * p2p_component_deinit() - P2P component de-init + * + * This function deregisters psoc/vdev create/delete handler. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_component_deinit(void); + +/** + * p2p_psoc_object_open() - Open P2P component + * @soc: soc context + * + * This function initialize p2p psoc object + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_psoc_object_open(struct wlan_objmgr_psoc *soc); + +/** + * p2p_psoc_object_close() - Close P2P component + * @soc: soc context + * + * This function de-init p2p psoc object. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_psoc_object_close(struct wlan_objmgr_psoc *soc); + +/** + * p2p_psoc_start() - Start P2P component + * @soc: soc context + * @req: P2P start parameters + * + * This function sets up layer call back in p2p psoc object + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_psoc_start(struct wlan_objmgr_psoc *soc, + struct p2p_start_param *req); + +/** + * p2p_psoc_stop() - Stop P2P component + * @soc: soc context + * + * This function clears up layer call back in p2p psoc object. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_psoc_stop(struct wlan_objmgr_psoc *soc); + +/** + * p2p_process_cmd() - Process P2P messages in OS interface queue + * @msg: message information + * + * This function is main handler for P2P messages in OS interface + * queue, it gets called by message scheduler. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_process_cmd(struct scheduler_msg *msg); + +/** + * p2p_process_evt() - Process P2P messages in target interface queue + * @msg: message information + * + * This function is main handler for P2P messages in target interface + * queue, it gets called by message scheduler. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_process_evt(struct scheduler_msg *msg); + +/** + * p2p_msg_flush_callback() - Callback used to flush P2P messages + * @msg: message information + * + * This callback will be called when scheduler flush some of P2P messages. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_msg_flush_callback(struct scheduler_msg *msg); + +/** + * p2p_event_flush_callback() - Callback used to flush P2P events + * @msg: event information + * + * This callback will be called when scheduler flush some of P2P events. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_event_flush_callback(struct scheduler_msg *msg); + +#ifdef FEATURE_P2P_LISTEN_OFFLOAD +/** + * p2p_process_lo_stop() - Process lo stop event + * @lo_stop_event: listen offload stop event information + * + * This function handles listen offload stop event and deliver this + * event to HDD layer by registered callback. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_process_lo_stop( + struct p2p_lo_stop_event *lo_stop_event); +#else +static inline QDF_STATUS p2p_process_lo_stop( + struct p2p_lo_stop_event *lo_stop_event) +{ + return QDF_STATUS_SUCCESS; +} +#endif +/** + * p2p_process_noa() - Process noa event + * @noa_event: noa event information + * + * This function handles noa event and save noa information in p2p + * vdev object. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_process_noa(struct p2p_noa_event *noa_event); + +#ifdef WLAN_FEATURE_P2P_DEBUG +/** + * p2p_status_scan() - Update P2P connection status + * @vdev: vdev context + * + * This function updates P2P connection status when scanning + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_status_scan(struct wlan_objmgr_vdev *vdev); + +/** + * p2p_status_connect() - Update P2P connection status + * @vdev: vdev context + * + * This function updates P2P connection status when connecting. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_status_connect(struct wlan_objmgr_vdev *vdev); + +/** + * p2p_status_disconnect() - Update P2P connection status + * @vdev: vdev context + * + * This function updates P2P connection status when disconnecting. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_status_disconnect(struct wlan_objmgr_vdev *vdev); + +/** + * p2p_status_start_bss() - Update P2P connection status + * @vdev: vdev context + * + * This function updates P2P connection status when starting BSS. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_status_start_bss(struct wlan_objmgr_vdev *vdev); + +/** + * p2p_status_stop_bss() - Update P2P connection status + * @vdev: vdev context + * + * This function updates P2P connection status when stopping BSS. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_status_stop_bss(struct wlan_objmgr_vdev *vdev); +#else +static inline QDF_STATUS p2p_status_scan(struct wlan_objmgr_vdev *vdev) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS p2p_status_connect(struct wlan_objmgr_vdev *vdev) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS p2p_status_disconnect(struct wlan_objmgr_vdev *vdev) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS p2p_status_start_bss(struct wlan_objmgr_vdev *vdev) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS p2p_status_stop_bss(struct wlan_objmgr_vdev *vdev) +{ + return QDF_STATUS_SUCCESS; +} +#endif /* WLAN_FEATURE_P2P_DEBUG */ +#endif /* _WLAN_P2P_MAIN_H_ */ diff --git a/p2p/core/src/wlan_p2p_off_chan_tx.c b/p2p/core/src/wlan_p2p_off_chan_tx.c new file mode 100644 index 0000000000..1309ccd08d --- /dev/null +++ b/p2p/core/src/wlan_p2p_off_chan_tx.c @@ -0,0 +1,2955 @@ +/* + * Copyright (c) 2017-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. + */ + +/** + * DOC: This file contains off channel tx API definitions + */ + +#include +#include +#include +#include +#include +#include +#include "wlan_p2p_public_struct.h" +#include "wlan_p2p_tgt_api.h" +#include "wlan_p2p_ucfg_api.h" +#include "wlan_p2p_roc.h" +#include "wlan_p2p_main.h" +#include "wlan_p2p_off_chan_tx.h" +#include "wlan_osif_request_manager.h" + +/** + * p2p_psoc_get_tx_ops() - get p2p tx ops + * @psoc: psoc object + * + * This function returns p2p tx ops callbacks. + * + * Return: wlan_lmac_if_p2p_tx_ops + */ +static inline struct wlan_lmac_if_p2p_tx_ops * +p2p_psoc_get_tx_ops(struct wlan_objmgr_psoc *psoc) +{ + return &psoc->soc_cb.tx_ops.p2p; +} + +/** + * p2p_tx_context_check_valid() - check tx action context + * @tx_ctx: tx context + * + * This function check if tx action context and parameters are valid. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_tx_context_check_valid(struct tx_action_context *tx_ctx) +{ + struct wlan_objmgr_psoc *psoc; + struct p2p_soc_priv_obj *p2p_soc_obj; + + if (!tx_ctx) { + p2p_err("null tx action context"); + return QDF_STATUS_E_INVAL; + } + + p2p_soc_obj = tx_ctx->p2p_soc_obj; + if (!p2p_soc_obj) { + p2p_err("null p2p soc private object"); + return QDF_STATUS_E_INVAL; + } + + psoc = p2p_soc_obj->soc; + if (!psoc) { + p2p_err("null p2p soc object"); + return QDF_STATUS_E_INVAL; + } + + if (!tx_ctx->buf) { + p2p_err("null tx buffer"); + return QDF_STATUS_E_INVAL; + } + + return QDF_STATUS_SUCCESS; +} + +/** + * p2p_vdev_check_valid() - check vdev and vdev mode + * @tx_ctx: tx context + * + * This function check if vdev and vdev mode are valid. It will drop + * probe response in sta mode. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_vdev_check_valid(struct tx_action_context *tx_ctx) +{ + enum QDF_OPMODE mode; + struct wlan_objmgr_vdev *vdev; + struct wlan_objmgr_psoc *psoc; + struct p2p_soc_priv_obj *p2p_soc_obj; + QDF_STATUS status = QDF_STATUS_SUCCESS; + + p2p_soc_obj = tx_ctx->p2p_soc_obj; + psoc = p2p_soc_obj->soc; + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, + tx_ctx->vdev_id, WLAN_P2P_ID); + if (!vdev) { + p2p_err("null vdev object"); + return QDF_STATUS_E_INVAL; + } + + mode = wlan_vdev_mlme_get_opmode(vdev); + p2p_debug("vdev mode:%d", mode); + + /* drop probe response for sta, go, sap */ + if ((mode == QDF_STA_MODE || + mode == QDF_SAP_MODE || + mode == QDF_P2P_GO_MODE) && + tx_ctx->frame_info.sub_type == P2P_MGMT_PROBE_RSP) { + p2p_debug("drop probe response, mode:%d", mode); + status = QDF_STATUS_E_FAILURE; + } + + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); + + return status; +} + +/** + * p2p_get_p2pie_ptr() - get the pointer to p2p ie + * @ie: source ie + * @ie_len: source ie length + * + * This function finds out p2p ie by p2p oui and return the pointer. + * + * Return: pointer to p2p ie + */ +static const uint8_t *p2p_get_p2pie_ptr(const uint8_t *ie, uint16_t ie_len) +{ + return wlan_get_vendor_ie_ptr_from_oui(P2P_OUI, + P2P_OUI_SIZE, ie, ie_len); +} + +/** + * p2p_get_p2pie_from_probe_rsp() - get the pointer to p2p ie from + * probe response + * @tx_ctx: tx context + * + * This function finds out p2p ie and return the pointer if it is a + * probe response frame. + * + * Return: pointer to p2p ie + */ +static const uint8_t *p2p_get_p2pie_from_probe_rsp( + struct tx_action_context *tx_ctx) +{ + const uint8_t *ie; + const uint8_t *p2p_ie; + const uint8_t *tmp_p2p_ie = NULL; + uint16_t ie_len; + + if (tx_ctx->buf_len <= PROBE_RSP_IE_OFFSET) { + p2p_err("Invalid header len for probe response"); + return NULL; + } + + ie = tx_ctx->buf + PROBE_RSP_IE_OFFSET; + ie_len = tx_ctx->buf_len - PROBE_RSP_IE_OFFSET; + p2p_ie = p2p_get_p2pie_ptr(ie, ie_len); + while ((p2p_ie) && + (P2P_MAX_IE_LENGTH == p2p_ie[1])) { + ie_len = tx_ctx->buf_len - (p2p_ie - tx_ctx->buf); + if (ie_len > 2) { + ie = p2p_ie + P2P_MAX_IE_LENGTH + 2; + tmp_p2p_ie = p2p_get_p2pie_ptr(ie, ie_len); + } + + if (tmp_p2p_ie) { + p2p_ie = tmp_p2p_ie; + tmp_p2p_ie = NULL; + } else { + break; + } + } + + return p2p_ie; +} + +/** + * p2p_get_presence_noa_attr() - get the pointer to noa attr + * @pies: source ie + * @length: source ie length + * + * This function finds out noa attr by noa eid and return the pointer. + * + * Return: pointer to noa attr + */ +static const uint8_t *p2p_get_presence_noa_attr(const uint8_t *pies, int length) +{ + int left = length; + const uint8_t *ptr = pies; + uint8_t elem_id; + uint16_t elem_len; + + p2p_debug("pies:%pK, length:%d", pies, length); + + while (left >= 3) { + elem_id = ptr[0]; + elem_len = ((uint16_t) ptr[1]) | (ptr[2] << 8); + + left -= 3; + if (elem_len > left) { + p2p_err("****Invalid IEs, elem_len=%d left=%d*****", + elem_len, left); + return NULL; + } + if (elem_id == P2P_NOA_ATTR) + return ptr; + + left -= elem_len; + ptr += (elem_len + 3); + } + + return NULL; +} + +/** + * p2p_get_noa_attr_stream_in_mult_p2p_ies() - get the pointer to noa + * attr from multi p2p ie + * @noa_stream: noa stream + * @noa_len: noa stream length + * @overflow_len: overflow length + * + * This function finds out noa attr from multi p2p ies. + * + * Return: noa length + */ +static uint8_t p2p_get_noa_attr_stream_in_mult_p2p_ies(uint8_t *noa_stream, + uint8_t noa_len, uint8_t overflow_len) +{ + uint8_t overflow_p2p_stream[P2P_MAX_NOA_ATTR_LEN]; + + p2p_debug("noa_stream:%pK, noa_len:%d, overflow_len:%d", + noa_stream, noa_len, overflow_len); + if ((noa_len <= (P2P_MAX_NOA_ATTR_LEN + P2P_IE_HEADER_LEN)) && + (noa_len >= overflow_len) && + (overflow_len <= P2P_MAX_NOA_ATTR_LEN)) { + qdf_mem_copy(overflow_p2p_stream, + noa_stream + noa_len - overflow_len, + overflow_len); + noa_stream[noa_len - overflow_len] = + P2P_EID_VENDOR; + noa_stream[noa_len - overflow_len + 1] = + overflow_len + P2P_OUI_SIZE; + qdf_mem_copy(noa_stream + noa_len - overflow_len + 2, + P2P_OUI, P2P_OUI_SIZE); + qdf_mem_copy(noa_stream + noa_len + 2 + P2P_OUI_SIZE - + overflow_len, overflow_p2p_stream, + overflow_len); + } + + return noa_len + P2P_IE_HEADER_LEN; +} + +/** + * p2p_get_vdev_noa_info() - get vdev noa information + * @tx_ctx: tx context + * + * This function gets vdev noa information + * + * Return: pointer to noa information + */ +static struct p2p_noa_info *p2p_get_vdev_noa_info( + struct tx_action_context *tx_ctx) +{ + 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; + struct p2p_noa_info *noa_info = NULL; + + p2p_soc_obj = tx_ctx->p2p_soc_obj; + psoc = p2p_soc_obj->soc; + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, + tx_ctx->vdev_id, WLAN_P2P_ID); + if (!vdev) { + p2p_err("vdev obj is NULL"); + return NULL; + } + + mode = wlan_vdev_mlme_get_opmode(vdev); + p2p_debug("vdev mode:%d", mode); + if (mode != QDF_P2P_GO_MODE) { + p2p_debug("invalid p2p vdev mode:%d", mode); + goto fail; + } + + p2p_vdev_obj = wlan_objmgr_vdev_get_comp_private_obj(vdev, + WLAN_UMAC_COMP_P2P); + + if (!p2p_vdev_obj || !(p2p_vdev_obj->noa_info)) { + p2p_debug("null noa info"); + goto fail; + } + + noa_info = p2p_vdev_obj->noa_info; + +fail: + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); + + return noa_info; +} + +/** + * p2p_get_noa_attr_stream() - get noa stream from p2p vdev object + * @tx_ctx: tx context + * @pnoa_stream: pointer to noa stream + * + * This function finds out noa stream from p2p vdev object + * + * Return: noa stream length + */ +static uint8_t p2p_get_noa_attr_stream( + struct tx_action_context *tx_ctx, uint8_t *pnoa_stream) +{ + struct p2p_noa_info *noa_info; + struct noa_descriptor *noa_desc_0; + struct noa_descriptor *noa_desc_1; + uint8_t *pbody = pnoa_stream; + uint8_t len = 0; + + noa_info = p2p_get_vdev_noa_info(tx_ctx); + if (!noa_info) { + p2p_debug("not valid noa information"); + return 0; + } + + noa_desc_0 = &(noa_info->noa_desc[0]); + noa_desc_1 = &(noa_info->noa_desc[1]); + if ((!(noa_desc_0->duration)) && + (!(noa_desc_1->duration)) && + (!noa_info->opps_ps)) { + p2p_debug("opps ps and duration are 0"); + return 0; + } + + pbody[0] = P2P_NOA_ATTR; + pbody[3] = noa_info->index; + pbody[4] = noa_info->ct_window | (noa_info->opps_ps << 7); + len = 5; + pbody += len; + + if (noa_desc_0->duration) { + *pbody = noa_desc_0->type_count; + pbody += 1; + len += 1; + + *((uint32_t *) (pbody)) = noa_desc_0->duration; + pbody += sizeof(uint32_t); + len += 4; + + *((uint32_t *) (pbody)) = noa_desc_0->interval; + pbody += sizeof(uint32_t); + len += 4; + + *((uint32_t *) (pbody)) = noa_desc_0->start_time; + pbody += sizeof(uint32_t); + len += 4; + } + + if (noa_desc_1->duration) { + *pbody = noa_desc_1->type_count; + pbody += 1; + len += 1; + + *((uint32_t *) (pbody)) = noa_desc_1->duration; + pbody += sizeof(uint32_t); + len += 4; + + *((uint32_t *) (pbody)) = noa_desc_1->interval; + pbody += sizeof(uint32_t); + len += 4; + + *((uint32_t *) (pbody)) = noa_desc_1->start_time; + pbody += sizeof(uint32_t); + len += 4; + } + + pbody = pnoa_stream + 1; + /* one byte for Attr and 2 bytes for length */ + *((uint16_t *) (pbody)) = len - 3; + + return len; +} + +/** + * p2p_update_noa_stream() - update noa stream + * @tx_ctx: tx context + * @p2p_ie: pointer to p2p ie + * @noa_attr: pointer to noa attr + * @total_len: pointer to total length of ie + * + * This function updates noa stream. + * + * Return: noa stream length + */ +static uint16_t p2p_update_noa_stream(struct tx_action_context *tx_ctx, + uint8_t *p2p_ie, const uint8_t *noa_attr, uint32_t *total_len, + uint8_t *noa_stream) +{ + uint16_t noa_len; + uint16_t overflow_len; + uint8_t orig_len; + uint32_t nbytes_copy; + uint32_t buf_len = *total_len; + + noa_len = p2p_get_noa_attr_stream(tx_ctx, noa_stream); + if (noa_len <= 0) { + p2p_debug("do not find out noa attr"); + return 0; + } + + orig_len = p2p_ie[1]; + if (noa_attr) { + noa_len = noa_attr[1] | (noa_attr[2] << 8); + orig_len -= (noa_len + 1 + 2); + buf_len -= (noa_len + 1 + 2); + p2p_ie[1] = orig_len; + } + + if ((p2p_ie[1] + noa_len) > P2P_MAX_IE_LENGTH) { + overflow_len = p2p_ie[1] + noa_len - + P2P_MAX_IE_LENGTH; + noa_len = p2p_get_noa_attr_stream_in_mult_p2p_ies( + noa_stream, noa_len, overflow_len); + p2p_ie[1] = P2P_MAX_IE_LENGTH; + } else { + /* increment the length of P2P IE */ + p2p_ie[1] += noa_len; + } + + *total_len = buf_len; + nbytes_copy = (p2p_ie + orig_len + 2) - tx_ctx->buf; + + p2p_debug("noa_len=%d orig_len=%d p2p_ie=%pK buf_len=%d nbytes copy=%d ", + noa_len, orig_len, p2p_ie, buf_len, nbytes_copy); + + return noa_len; +} + +/** + * p2p_set_ht_caps() - set ht capability + * @tx_ctx: tx context + * @num_bytes: number bytes + * + * This function sets ht capability + * + * Return: None + */ +static void p2p_set_ht_caps(struct tx_action_context *tx_ctx, + uint32_t num_bytes) +{ +} + +/** + * p2p_populate_mac_header() - update sequence number + * @tx_ctx: tx context + * + * This function updates sequence number of this mgmt frame + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_populate_mac_header( + struct tx_action_context *tx_ctx) +{ + struct wlan_seq_ctl *seq_ctl; + struct wlan_frame_hdr *wh; + struct wlan_objmgr_peer *peer; + struct wlan_objmgr_psoc *psoc; + void *mac_addr; + uint16_t seq_num; + uint8_t pdev_id; + struct wlan_objmgr_vdev *vdev; + + psoc = tx_ctx->p2p_soc_obj->soc; + + wh = (struct wlan_frame_hdr *)tx_ctx->buf; + mac_addr = wh->i_addr1; + pdev_id = wlan_get_pdev_id_from_vdev_id(psoc, tx_ctx->vdev_id, + WLAN_P2P_ID); + peer = wlan_objmgr_get_peer(psoc, pdev_id, mac_addr, WLAN_P2P_ID); + if (!peer) { + mac_addr = wh->i_addr2; + peer = wlan_objmgr_get_peer(psoc, pdev_id, mac_addr, + WLAN_P2P_ID); + } + if (!peer && tx_ctx->rand_mac_tx) { + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc, + tx_ctx->vdev_id, + WLAN_P2P_ID); + if (vdev) { + mac_addr = wlan_vdev_mlme_get_macaddr(vdev); + peer = wlan_objmgr_get_peer(psoc, pdev_id, mac_addr, + WLAN_P2P_ID); + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); + } + } + if (!peer) { + p2p_err("no valid peer"); + return QDF_STATUS_E_INVAL; + } + seq_num = (uint16_t)wlan_peer_mlme_get_next_seq_num(peer); + seq_ctl = (struct wlan_seq_ctl *)(tx_ctx->buf + + WLAN_SEQ_CTL_OFFSET); + seq_ctl->seq_num_lo = (seq_num & WLAN_LOW_SEQ_NUM_MASK); + seq_ctl->seq_num_hi = ((seq_num & WLAN_HIGH_SEQ_NUM_MASK) >> + WLAN_HIGH_SEQ_NUM_OFFSET); + + wlan_objmgr_peer_release_ref(peer, WLAN_P2P_ID); + + return QDF_STATUS_SUCCESS; +} + +/** + * p2p_get_frame_type_str() - parse frame type to string + * @frame_info: frame information + * + * This function parse frame type to string. + * + * Return: command string + */ +static char *p2p_get_frame_type_str(struct p2p_frame_info *frame_info) +{ + if (frame_info->type == P2P_FRAME_NOT_SUPPORT) + return "Not support frame"; + + if (frame_info->sub_type == P2P_MGMT_NOT_SUPPORT) + return "Not support sub frame"; + + switch (frame_info->sub_type) { + case P2P_MGMT_PROBE_REQ: + return "P2P roc request"; + case P2P_MGMT_PROBE_RSP: + return "P2P cancel roc request"; + case P2P_MGMT_ACTION: + break; + default: + return "Invalid P2P command"; + } + + if (frame_info->action_type == P2P_ACTION_PRESENCE_REQ) + return "P2P action presence request"; + if (frame_info->action_type == P2P_ACTION_PRESENCE_RSP) + return "P2P action presence response"; + + switch (frame_info->public_action_type) { + case P2P_PUBLIC_ACTION_NEG_REQ: + return "GO negotiation request frame"; + case P2P_PUBLIC_ACTION_NEG_RSP: + return "GO negotiation response frame"; + case P2P_PUBLIC_ACTION_NEG_CNF: + return "GO negotiation confirm frame"; + case P2P_PUBLIC_ACTION_INVIT_REQ: + return "P2P invitation request"; + case P2P_PUBLIC_ACTION_INVIT_RSP: + return "P2P invitation response"; + case P2P_PUBLIC_ACTION_DEV_DIS_REQ: + return "Device discoverability request"; + case P2P_PUBLIC_ACTION_DEV_DIS_RSP: + return "Device discoverability response"; + case P2P_PUBLIC_ACTION_PROV_DIS_REQ: + return "Provision discovery request"; + case P2P_PUBLIC_ACTION_PROV_DIS_RSP: + return "Provision discovery response"; + case P2P_PUBLIC_ACTION_GAS_INIT_REQ: + return "GAS init request"; + case P2P_PUBLIC_ACTION_GAS_INIT_RSP: + return "GAS init response"; + case P2P_PUBLIC_ACTION_GAS_COMB_REQ: + return "GAS come back request"; + case P2P_PUBLIC_ACTION_GAS_COMB_RSP: + return "GAS come back response"; + default: + return "Not support action frame"; + } +} + +/** + * p2p_init_frame_info() - init frame information structure + * @frame_info: pointer to frame information + * + * This function init frame information structure. + * + * Return: None + */ +static void p2p_init_frame_info(struct p2p_frame_info *frame_info) +{ + frame_info->type = P2P_FRAME_NOT_SUPPORT; + frame_info->sub_type = P2P_MGMT_NOT_SUPPORT; + frame_info->public_action_type = + P2P_PUBLIC_ACTION_NOT_SUPPORT; + frame_info->action_type = P2P_ACTION_NOT_SUPPORT; +} + +/** + * p2p_get_frame_info() - get frame information from packet + * @data_buf: data buffer address + * @length: buffer length + * @frame_info: frame information + * + * This function gets frame information from packet. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_get_frame_info(uint8_t *data_buf, uint32_t length, + struct p2p_frame_info *frame_info) +{ + uint8_t type; + uint8_t sub_type; + uint8_t action_type; + uint8_t *buf = data_buf; + + p2p_init_frame_info(frame_info); + + if (length < P2P_ACTION_OFFSET + 1) { + p2p_err("invalid p2p mgmt hdr len"); + return QDF_STATUS_E_INVAL; + } + + type = P2P_GET_TYPE_FRM_FC(buf[0]); + sub_type = P2P_GET_SUBTYPE_FRM_FC(buf[0]); + if (type != P2P_FRAME_MGMT) { + p2p_err("just support mgmt frame"); + return QDF_STATUS_E_FAILURE; + } + + frame_info->type = P2P_FRAME_MGMT; + if (sub_type != P2P_MGMT_PROBE_RSP && + sub_type != P2P_MGMT_ACTION) { + p2p_err("not support sub type"); + return QDF_STATUS_E_FAILURE; + } + + if (sub_type == P2P_MGMT_PROBE_RSP) { + frame_info->sub_type = P2P_MGMT_PROBE_RSP; + p2p_debug("Probe Response"); + return QDF_STATUS_SUCCESS; + } + + if (sub_type == P2P_MGMT_PROBE_REQ) { + frame_info->sub_type = P2P_MGMT_PROBE_REQ; + p2p_debug("Probe Request"); + return QDF_STATUS_SUCCESS; + } + + frame_info->sub_type = P2P_MGMT_ACTION; + buf += P2P_ACTION_OFFSET; + if (length > P2P_PUBLIC_ACTION_FRAME_TYPE_OFFSET && + buf[0] == P2P_PUBLIC_ACTION_FRAME && + buf[1] == P2P_PUBLIC_ACTION_VENDOR_SPECIFIC && + !qdf_mem_cmp(&buf[2], P2P_OUI, P2P_OUI_SIZE)) { + buf = data_buf + + P2P_PUBLIC_ACTION_FRAME_TYPE_OFFSET; + action_type = buf[0]; + if (action_type > P2P_PUBLIC_ACTION_PROV_DIS_RSP) + frame_info->public_action_type = + P2P_PUBLIC_ACTION_NOT_SUPPORT; + else + frame_info->public_action_type = action_type; + } else if (length > P2P_ACTION_FRAME_TYPE_OFFSET && + buf[0] == P2P_ACTION_VENDOR_SPECIFIC_CATEGORY && + !qdf_mem_cmp(&buf[1], P2P_OUI, P2P_OUI_SIZE)) { + buf = data_buf + + P2P_ACTION_FRAME_TYPE_OFFSET; + action_type = buf[0]; + if (action_type == P2P_ACTION_PRESENCE_REQ) + frame_info->action_type = + P2P_ACTION_PRESENCE_REQ; + if (action_type == P2P_ACTION_PRESENCE_RSP) + frame_info->action_type = + P2P_ACTION_PRESENCE_RSP; + } else { + p2p_debug("this is not vendor specific p2p action frame"); + return QDF_STATUS_SUCCESS; + } + + p2p_debug("%s", p2p_get_frame_type_str(frame_info)); + + return QDF_STATUS_SUCCESS; +} + +#ifdef WLAN_FEATURE_P2P_DEBUG +/** + * p2p_tx_update_connection_status() - Update P2P connection status + * with tx frame + * @p2p_soc_obj: P2P soc private object + * @tx_frame_info: frame information + * @mac_to: Pointer to dest MAC address + * + * This function updates P2P connection status with tx frame. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_tx_update_connection_status( + struct p2p_soc_priv_obj *p2p_soc_obj, + struct p2p_frame_info *tx_frame_info, + uint8_t *mac_to) +{ + if (!p2p_soc_obj || !tx_frame_info || !mac_to) { + p2p_err("invalid p2p_soc_obj:%pK or tx_frame_info:%pK or mac_to:%pK", + p2p_soc_obj, tx_frame_info, mac_to); + return QDF_STATUS_E_INVAL; + } + + if (tx_frame_info->public_action_type != + P2P_PUBLIC_ACTION_NOT_SUPPORT) + p2p_info("%s ---> OTA to " QDF_MAC_ADDR_STR, + p2p_get_frame_type_str(tx_frame_info), + QDF_MAC_ADDR_ARRAY(mac_to)); + + if ((tx_frame_info->public_action_type == + P2P_PUBLIC_ACTION_PROV_DIS_REQ) && + (p2p_soc_obj->connection_status == P2P_NOT_ACTIVE)) { + p2p_soc_obj->connection_status = P2P_GO_NEG_PROCESS; + p2p_info("[P2P State]Inactive state to GO negotiation progress state"); + } else if ((tx_frame_info->public_action_type == + P2P_PUBLIC_ACTION_NEG_CNF) && + (p2p_soc_obj->connection_status == + P2P_GO_NEG_PROCESS)) { + p2p_soc_obj->connection_status = P2P_GO_NEG_COMPLETED; + p2p_info("[P2P State]GO nego progress to GO nego completed state"); + } + + return QDF_STATUS_SUCCESS; +} + +/** + * p2p_rx_update_connection_status() - Update P2P connection status + * with rx frame + * @p2p_soc_obj: P2P soc private object + * @rx_frame_info: frame information + * @mac_from: Pointer to source MAC address + * + * This function updates P2P connection status with rx frame. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_rx_update_connection_status( + struct p2p_soc_priv_obj *p2p_soc_obj, + struct p2p_frame_info *rx_frame_info, + uint8_t *mac_from) +{ + if (!p2p_soc_obj || !rx_frame_info || !mac_from) { + p2p_err("invalid p2p_soc_obj:%pK or rx_frame_info:%pK, mac_from:%pK", + p2p_soc_obj, rx_frame_info, mac_from); + return QDF_STATUS_E_INVAL; + } + + if (rx_frame_info->public_action_type != + P2P_PUBLIC_ACTION_NOT_SUPPORT) + p2p_info("%s <--- OTA from " QDF_MAC_ADDR_STR, + p2p_get_frame_type_str(rx_frame_info), + QDF_MAC_ADDR_ARRAY(mac_from)); + + if ((rx_frame_info->public_action_type == + P2P_PUBLIC_ACTION_PROV_DIS_REQ) && + (p2p_soc_obj->connection_status == P2P_NOT_ACTIVE)) { + p2p_soc_obj->connection_status = P2P_GO_NEG_PROCESS; + p2p_info("[P2P State]Inactive state to GO negotiation progress state"); + } else if ((rx_frame_info->public_action_type == + P2P_PUBLIC_ACTION_NEG_CNF) && + (p2p_soc_obj->connection_status == + P2P_GO_NEG_PROCESS)) { + p2p_soc_obj->connection_status = P2P_GO_NEG_COMPLETED; + p2p_info("[P2P State]GO negotiation progress to GO negotiation completed state"); + } else if ((rx_frame_info->public_action_type == + P2P_PUBLIC_ACTION_INVIT_REQ) && + (p2p_soc_obj->connection_status == P2P_NOT_ACTIVE)) { + p2p_soc_obj->connection_status = P2P_GO_NEG_COMPLETED; + p2p_info("[P2P State]Inactive state to GO negotiation completed state Autonomous GO formation"); + } + + return QDF_STATUS_SUCCESS; +} +#else +static QDF_STATUS p2p_tx_update_connection_status( + struct p2p_soc_priv_obj *p2p_soc_obj, + struct p2p_frame_info *tx_frame_info, + uint8_t *mac_to) +{ + return QDF_STATUS_SUCCESS; +} + +static QDF_STATUS p2p_rx_update_connection_status( + struct p2p_soc_priv_obj *p2p_soc_obj, + struct p2p_frame_info *rx_frame_info, + uint8_t *mac_from) +{ + return QDF_STATUS_SUCCESS; +} +#endif + +/** + * p2p_packet_alloc() - allocate qdf nbuf + * @size: buffe size + * @data: pointer to qdf nbuf data point + * @ppPacket: pointer to qdf nbuf point + * + * This function allocates qdf nbuf. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_packet_alloc(uint16_t size, void **data, + qdf_nbuf_t *ppPacket) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + qdf_nbuf_t nbuf; + + nbuf = qdf_nbuf_alloc(NULL, + roundup(size + P2P_TX_PKT_MIN_HEADROOM, 4), + P2P_TX_PKT_MIN_HEADROOM, sizeof(uint32_t), + false); + + if (nbuf != NULL) { + qdf_nbuf_put_tail(nbuf, size); + qdf_nbuf_set_protocol(nbuf, ETH_P_CONTROL); + *ppPacket = nbuf; + *data = qdf_nbuf_data(nbuf); + qdf_mem_set(*data, size, 0); + status = QDF_STATUS_SUCCESS; + } + + return status; +} + +/** + * p2p_send_tx_conf() - send tx confirm + * @tx_ctx: tx context + * @status: tx status + * + * This function send tx confirm to osif + * + * Return: QDF_STATUS_SUCCESS - pointer to tx context + */ +static QDF_STATUS p2p_send_tx_conf(struct tx_action_context *tx_ctx, + bool status) +{ + struct p2p_tx_cnf tx_cnf; + struct p2p_soc_priv_obj *p2p_soc_obj; + struct p2p_start_param *start_param; + + p2p_soc_obj = tx_ctx->p2p_soc_obj; + + if (!p2p_soc_obj || !(p2p_soc_obj->start_param)) { + p2p_err("Invalid p2p soc object or start parameters"); + return QDF_STATUS_E_INVAL; + } + + start_param = p2p_soc_obj->start_param; + if (!(start_param->tx_cnf_cb)) { + p2p_err("no tx confirm callback"); + return QDF_STATUS_E_INVAL; + } + + if (tx_ctx->no_ack) + tx_cnf.action_cookie = 0; + else + tx_cnf.action_cookie = (uint64_t)tx_ctx->id; + + tx_cnf.vdev_id = tx_ctx->vdev_id; + tx_cnf.buf = tx_ctx->buf; + tx_cnf.buf_len = tx_ctx->buf_len; + tx_cnf.status = status ? 0 : 1; + + p2p_debug("soc:%pK, vdev_id:%d, action_cookie:%llx, len:%d, status:%d, buf:%pK", + p2p_soc_obj->soc, tx_cnf.vdev_id, + tx_cnf.action_cookie, tx_cnf.buf_len, + tx_cnf.status, tx_cnf.buf); + + p2p_rand_mac_tx_done(p2p_soc_obj->soc, tx_ctx); + + start_param->tx_cnf_cb(start_param->tx_cnf_cb_data, &tx_cnf); + + return QDF_STATUS_SUCCESS; +} + +/** + * p2p_mgmt_tx() - call mgmt tx api + * @tx_ctx: tx context + * @buf_len: buffer length + * @packet: pointer to qdf nbuf + * @frame: pointer to qdf nbuf data + * + * This function call mgmt tx api to tx this action frame. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_mgmt_tx(struct tx_action_context *tx_ctx, + uint32_t buf_len, qdf_nbuf_t packet, uint8_t *frame) +{ + QDF_STATUS status; + mgmt_tx_download_comp_cb tx_comp_cb; + mgmt_ota_comp_cb tx_ota_comp_cb; + struct wlan_frame_hdr *wh; + struct wlan_objmgr_peer *peer; + struct wmi_mgmt_params mgmt_param = { 0 }; + struct wlan_objmgr_psoc *psoc; + void *mac_addr; + uint8_t pdev_id; + struct wlan_objmgr_vdev *vdev; + + psoc = tx_ctx->p2p_soc_obj->soc; + mgmt_param.tx_frame = packet; + mgmt_param.frm_len = buf_len; + mgmt_param.vdev_id = tx_ctx->vdev_id; + mgmt_param.pdata = frame; + mgmt_param.chanfreq = wlan_chan_to_freq(tx_ctx->chan); + + mgmt_param.qdf_ctx = wlan_psoc_get_qdf_dev(psoc); + if (!(mgmt_param.qdf_ctx)) { + p2p_err("qdf ctx is null"); + return QDF_STATUS_E_INVAL; + } + + wh = (struct wlan_frame_hdr *)frame; + mac_addr = wh->i_addr1; + pdev_id = wlan_get_pdev_id_from_vdev_id(psoc, tx_ctx->vdev_id, + WLAN_P2P_ID); + peer = wlan_objmgr_get_peer(psoc, pdev_id, mac_addr, WLAN_P2P_ID); + if (!peer) { + mac_addr = wh->i_addr2; + peer = wlan_objmgr_get_peer(psoc, pdev_id, mac_addr, + WLAN_P2P_ID); + } + if (!peer && tx_ctx->rand_mac_tx) { + vdev = wlan_objmgr_get_vdev_by_id_from_psoc( + psoc, tx_ctx->vdev_id, WLAN_P2P_ID); + if (vdev) { + mac_addr = wlan_vdev_mlme_get_macaddr(vdev); + peer = wlan_objmgr_get_peer(psoc, pdev_id, mac_addr, + WLAN_P2P_ID); + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); + } + } + + if (!peer) { + p2p_err("no valid peer"); + return QDF_STATUS_E_INVAL; + } + + if (tx_ctx->no_ack) { + tx_comp_cb = tgt_p2p_mgmt_download_comp_cb; + tx_ota_comp_cb = NULL; + } else { + tx_comp_cb = NULL; + tx_ota_comp_cb = tgt_p2p_mgmt_ota_comp_cb; + } + + p2p_debug("length:%d, vdev_id:%d, chanfreq:%d, no_ack:%d", + mgmt_param.frm_len, mgmt_param.vdev_id, + mgmt_param.chanfreq, tx_ctx->no_ack); + + tx_ctx->nbuf = packet; + + status = wlan_mgmt_txrx_mgmt_frame_tx(peer, tx_ctx->p2p_soc_obj, + packet, tx_comp_cb, tx_ota_comp_cb, + WLAN_UMAC_COMP_P2P, &mgmt_param); + + wlan_objmgr_peer_release_ref(peer, WLAN_P2P_ID); + + return status; +} + +/** + * p2p_roc_req_for_tx_action() - new a roc request for tx + * @tx_ctx: tx context + * + * This function new a roc request for tx and call roc api to process + * this new roc request. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_roc_req_for_tx_action( + struct tx_action_context *tx_ctx) +{ + struct p2p_soc_priv_obj *p2p_soc_obj; + struct p2p_roc_context *roc_ctx; + QDF_STATUS status; + + roc_ctx = qdf_mem_malloc(sizeof(struct p2p_roc_context)); + if (!roc_ctx) { + p2p_err("Failed to allocate p2p roc context"); + return QDF_STATUS_E_NOMEM; + } + + p2p_soc_obj = tx_ctx->p2p_soc_obj; + roc_ctx->p2p_soc_obj = p2p_soc_obj; + roc_ctx->vdev_id = tx_ctx->vdev_id; + roc_ctx->chan = tx_ctx->chan; + roc_ctx->duration = tx_ctx->duration; + roc_ctx->roc_state = ROC_STATE_IDLE; + roc_ctx->roc_type = OFF_CHANNEL_TX; + roc_ctx->tx_ctx = tx_ctx; + roc_ctx->id = tx_ctx->id; + tx_ctx->roc_cookie = (uintptr_t)roc_ctx; + + p2p_debug("create roc request for off channel tx, tx ctx:%pK, roc ctx:%pK", + tx_ctx, roc_ctx); + + status = p2p_process_roc_req(roc_ctx); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("request roc for tx action frrame fail"); + return status; + } + + status = qdf_list_insert_back(&p2p_soc_obj->tx_q_roc, + &tx_ctx->node); + if (status != QDF_STATUS_SUCCESS) + p2p_err("Failed to insert off chan tx context to wait roc req queue"); + + return status; +} + +/** + * p2p_find_tx_ctx() - find tx context by cookie + * @p2p_soc_obj: p2p soc object + * @cookie: cookie to this p2p tx context + * @is_roc_q: it is in waiting for roc queue + * @is_ack_q: it is in waiting for ack queue + * + * This function finds out tx context by cookie. + * + * Return: pointer to tx context + */ +static struct tx_action_context *p2p_find_tx_ctx( + struct p2p_soc_priv_obj *p2p_soc_obj, uint64_t cookie, + bool *is_roc_q, bool *is_ack_q) +{ + struct tx_action_context *cur_tx_ctx; + qdf_list_node_t *p_node; + QDF_STATUS status; + *is_roc_q = false; + *is_ack_q = false; + + p2p_debug("Start to find tx ctx, p2p soc_obj:%pK, cookie:%llx", + p2p_soc_obj, cookie); + + status = qdf_list_peek_front(&p2p_soc_obj->tx_q_roc, &p_node); + while (QDF_IS_STATUS_SUCCESS(status)) { + cur_tx_ctx = qdf_container_of(p_node, + struct tx_action_context, node); + if ((uintptr_t) cur_tx_ctx == cookie) { + *is_roc_q = true; + p2p_debug("find tx ctx, cookie:%llx", cookie); + return cur_tx_ctx; + } + status = qdf_list_peek_next(&p2p_soc_obj->tx_q_roc, + p_node, &p_node); + } + + status = qdf_list_peek_front(&p2p_soc_obj->tx_q_ack, &p_node); + while (QDF_IS_STATUS_SUCCESS(status)) { + cur_tx_ctx = qdf_container_of(p_node, + struct tx_action_context, node); + if ((uintptr_t) cur_tx_ctx == cookie) { + *is_ack_q = true; + p2p_debug("find tx ctx, cookie:%llx", cookie); + return cur_tx_ctx; + } + status = qdf_list_peek_next(&p2p_soc_obj->tx_q_ack, + p_node, &p_node); + } + + return NULL; +} + +/** + * p2p_find_tx_ctx_by_roc() - find tx context by roc + * @p2p_soc_obj: p2p soc object + * @cookie: cookie to roc context + * + * This function finds out tx context by roc context. + * + * Return: pointer to tx context + */ +static struct tx_action_context *p2p_find_tx_ctx_by_roc( + struct p2p_soc_priv_obj *p2p_soc_obj, uint64_t cookie) +{ + struct tx_action_context *cur_tx_ctx; + qdf_list_node_t *p_node; + QDF_STATUS status; + + p2p_debug("Start to find tx ctx, p2p soc_obj:%pK, cookie:%llx", + p2p_soc_obj, cookie); + + status = qdf_list_peek_front(&p2p_soc_obj->tx_q_roc, &p_node); + while (QDF_IS_STATUS_SUCCESS(status)) { + cur_tx_ctx = qdf_container_of(p_node, + struct tx_action_context, node); + if (cur_tx_ctx->roc_cookie == cookie) { + p2p_debug("find tx ctx, cookie:%llx", cookie); + return cur_tx_ctx; + } + status = qdf_list_peek_next(&p2p_soc_obj->tx_q_roc, + p_node, &p_node); + } + + return NULL; +} + +/** + * p2p_move_tx_context_to_ack_queue() - move tx context to tx_q_ack + * @tx_ctx: tx context + * + * This function moves tx context to waiting for ack queue. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_move_tx_context_to_ack_queue( + struct tx_action_context *tx_ctx) +{ + bool is_roc_q = false; + bool is_ack_q = false; + struct p2p_soc_priv_obj *p2p_soc_obj = tx_ctx->p2p_soc_obj; + struct tx_action_context *cur_tx_ctx; + QDF_STATUS status; + + p2p_debug("move tx context to wait for roc queue, %pK", tx_ctx); + + cur_tx_ctx = p2p_find_tx_ctx(p2p_soc_obj, (uintptr_t)tx_ctx, + &is_roc_q, &is_ack_q); + if (cur_tx_ctx) { + if (is_roc_q) { + p2p_debug("find in wait for roc queue"); + status = qdf_list_remove_node( + &p2p_soc_obj->tx_q_roc, + (qdf_list_node_t *)tx_ctx); + if (status != QDF_STATUS_SUCCESS) + p2p_err("Failed to remove off chan tx context from wait roc req queue"); + } + + if (is_ack_q) { + p2p_debug("Already in waiting for ack queue"); + return QDF_STATUS_SUCCESS; + } + } + + status = qdf_list_insert_back( + &p2p_soc_obj->tx_q_ack, + &tx_ctx->node); + if (status != QDF_STATUS_SUCCESS) + p2p_err("Failed to insert off chan tx context to wait ack req queue"); + p2p_debug("insert tx context to wait for ack queue, status:%d", + status); + + return status; +} + +/** + * p2p_extend_roc_timer() - extend roc timer + * @p2p_soc_obj: p2p soc private object + * @frame_info: pointer to frame information + * + * This function extends roc timer for some of p2p public action frame. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_extend_roc_timer( + struct p2p_soc_priv_obj *p2p_soc_obj, + struct p2p_frame_info *frame_info) +{ + struct p2p_roc_context *curr_roc_ctx; + uint32_t extend_time; + + curr_roc_ctx = p2p_find_current_roc_ctx(p2p_soc_obj); + if (!curr_roc_ctx) { + p2p_debug("no running roc request currently"); + return QDF_STATUS_SUCCESS; + } + + if (!frame_info) { + p2p_err("invalid frame information"); + return QDF_STATUS_E_INVAL; + } + + switch (frame_info->public_action_type) { + case P2P_PUBLIC_ACTION_NEG_REQ: + case P2P_PUBLIC_ACTION_NEG_RSP: + extend_time = 2 * P2P_ACTION_FRAME_DEFAULT_WAIT; + break; + case P2P_PUBLIC_ACTION_INVIT_REQ: + case P2P_PUBLIC_ACTION_DEV_DIS_REQ: + extend_time = P2P_ACTION_FRAME_DEFAULT_WAIT; + break; + default: + extend_time = 0; + break; + } + + if (extend_time) { + p2p_debug("extend roc timer, duration:%d", extend_time); + curr_roc_ctx->duration = extend_time; + return p2p_restart_roc_timer(curr_roc_ctx); + } + + return QDF_STATUS_SUCCESS; +} + +/** + * p2p_adjust_tx_wait() - adjust tx wait + * @tx_ctx: tx context + * + * This function adjust wait time of this tx context + * + * Return: None + */ +static void p2p_adjust_tx_wait(struct tx_action_context *tx_ctx) +{ + struct p2p_frame_info *frame_info; + + frame_info = &(tx_ctx->frame_info); + switch (frame_info->public_action_type) { + case P2P_PUBLIC_ACTION_NEG_RSP: + case P2P_PUBLIC_ACTION_PROV_DIS_RSP: + tx_ctx->duration += P2P_ACTION_FRAME_RSP_WAIT; + break; + case P2P_PUBLIC_ACTION_NEG_CNF: + case P2P_PUBLIC_ACTION_INVIT_RSP: + tx_ctx->duration += P2P_ACTION_FRAME_ACK_WAIT; + break; + default: + break; + } +} + +/** + * p2p_remove_tx_context() - remove tx ctx from queue + * @tx_ctx: tx context + * + * This function remove tx context from waiting for roc queue or + * waiting for ack queue. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_remove_tx_context( + struct tx_action_context *tx_ctx) +{ + bool is_roc_q = false; + bool is_ack_q = false; + struct tx_action_context *cur_tx_ctx; + uint64_t cookie = (uintptr_t)tx_ctx; + struct p2p_soc_priv_obj *p2p_soc_obj = tx_ctx->p2p_soc_obj; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + p2p_debug("tx context:%pK", tx_ctx); + + cur_tx_ctx = p2p_find_tx_ctx(p2p_soc_obj, cookie, &is_roc_q, + &is_ack_q); + + /* for not off channel tx case, won't find from queue */ + if (!cur_tx_ctx) { + p2p_debug("Do not find tx context from queue"); + goto end; + } + + if (is_roc_q) { + status = qdf_list_remove_node( + &p2p_soc_obj->tx_q_roc, + (qdf_list_node_t *)cur_tx_ctx); + if (status != QDF_STATUS_SUCCESS) + p2p_err("Failed to tx context from wait roc req queue"); + } + + if (is_ack_q) { + status = qdf_list_remove_node( + &p2p_soc_obj->tx_q_ack, + (qdf_list_node_t *)cur_tx_ctx); + if (status != QDF_STATUS_SUCCESS) + p2p_err("Failed to tx context from wait ack req queue"); + } + +end: + if (!tx_ctx->roc_cookie) + qdf_idr_remove(&p2p_soc_obj->p2p_idr, tx_ctx->id); + qdf_mem_free(tx_ctx->buf); + qdf_mem_free(tx_ctx); + + return status; +} + +/** + * p2p_tx_timeout() - Callback for tx timeout + * @pdata: pointer to tx context + * + * This function is callback for tx time out. + * + * Return: None + */ +static void p2p_tx_timeout(void *pdata) +{ + struct tx_action_context *tx_ctx = pdata; + + p2p_info("pdata:%pK", pdata); + + if (!tx_ctx || !(tx_ctx->p2p_soc_obj)) { + p2p_err("invalid tx context or p2p soc object"); + return; + } + + qdf_mc_timer_destroy(&tx_ctx->tx_timer); + p2p_send_tx_conf(tx_ctx, false); + p2p_remove_tx_context(tx_ctx); +} + +/** + * p2p_enable_tx_timer() - enable tx timer + * @tx_ctx: tx context + * + * This function enable tx timer for action frame required ota tx. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_enable_tx_timer(struct tx_action_context *tx_ctx) +{ + QDF_STATUS status; + + p2p_debug("tx context:%pK", tx_ctx); + + status = qdf_mc_timer_init(&tx_ctx->tx_timer, + QDF_TIMER_TYPE_SW, p2p_tx_timeout, + tx_ctx); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("failed to init tx timer"); + return status; + } + + status = qdf_mc_timer_start(&tx_ctx->tx_timer, + P2P_ACTION_FRAME_TX_TIMEOUT); + if (status != QDF_STATUS_SUCCESS) + p2p_err("tx timer start failed"); + + return status; +} + +/** + * p2p_disable_tx_timer() - disable tx timer + * @tx_ctx: tx context + * + * This function disable tx timer for action frame required ota tx. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_disable_tx_timer(struct tx_action_context *tx_ctx) +{ + QDF_STATUS status; + + p2p_debug("tx context:%pK", tx_ctx); + + status = qdf_mc_timer_stop(&tx_ctx->tx_timer); + if (status != QDF_STATUS_SUCCESS) + p2p_err("Failed to stop tx timer, status:%d", status); + + status = qdf_mc_timer_destroy(&tx_ctx->tx_timer); + if (status != QDF_STATUS_SUCCESS) + p2p_err("Failed to destroy tx timer, status:%d", status); + + return status; +} + +/** + * is_rmf_mgmt_action_frame() - check RMF action frame by category + * @action_category: action frame actegory + * + * This function check the frame is robust mgmt action frame or not + * + * Return: true - if category is robust mgmt type + */ +static bool is_rmf_mgmt_action_frame(uint8_t action_category) +{ + switch (action_category) { + case ACTION_CATEGORY_SPECTRUM_MGMT: + case ACTION_CATEGORY_QOS: + case ACTION_CATEGORY_DLS: + case ACTION_CATEGORY_BACK: + case ACTION_CATEGORY_RRM: + case ACTION_FAST_BSS_TRNST: + case ACTION_CATEGORY_SA_QUERY: + case ACTION_CATEGORY_PROTECTED_DUAL_OF_PUBLIC_ACTION: + case ACTION_CATEGORY_WNM: + case ACTION_CATEGORY_MESH_ACTION: + case ACTION_CATEGORY_MULTIHOP_ACTION: + case ACTION_CATEGORY_DMG: + case ACTION_CATEGORY_FST: + case ACTION_CATEGORY_VENDOR_SPECIFIC_PROTECTED: + return true; + default: + break; + } + return false; +} + +/** + * p2p_populate_rmf_field() - populate unicast rmf frame + * @tx_ctx: tx_action_context + * @size: input size of frame, and output new size + * @ppbuf: input frame ptr, and output new frame + * @ppkt: input pkt, output new pkt. + * + * This function allocates new pkt for rmf frame. The + * new frame has extra space for ccmp field. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_populate_rmf_field(struct tx_action_context *tx_ctx, + uint32_t *size, uint8_t **ppbuf, qdf_nbuf_t *ppkt) +{ + struct wlan_frame_hdr *wh, *rmf_wh; + struct action_frm_hdr *action_hdr; + QDF_STATUS status = QDF_STATUS_SUCCESS; + qdf_nbuf_t pkt = NULL; + uint8_t *frame; + uint32_t frame_len; + struct p2p_soc_priv_obj *p2p_soc_obj; + + p2p_soc_obj = tx_ctx->p2p_soc_obj; + + if (tx_ctx->frame_info.sub_type != P2P_MGMT_ACTION || + !p2p_soc_obj->p2p_cb.is_mgmt_protected) + return QDF_STATUS_SUCCESS; + if (*size < (sizeof(struct wlan_frame_hdr) + + sizeof(struct action_frm_hdr))) { + return QDF_STATUS_E_INVAL; + } + + wh = (struct wlan_frame_hdr *)(*ppbuf); + action_hdr = (struct action_frm_hdr *)(*ppbuf + sizeof(*wh)); + + if (!is_rmf_mgmt_action_frame(action_hdr->action_category)) { + p2p_debug("non rmf act frame 0x%x cat %x", + tx_ctx->frame_info.sub_type, + action_hdr->action_category); + return QDF_STATUS_SUCCESS; + } + + if (!p2p_soc_obj->p2p_cb.is_mgmt_protected( + tx_ctx->vdev_id, wh->i_addr1)) { + p2p_debug("non rmf connection vdev %d "QDF_MAC_ADDR_STR, + tx_ctx->vdev_id, QDF_MAC_ADDR_ARRAY(wh->i_addr1)); + return QDF_STATUS_SUCCESS; + } + if (!qdf_is_macaddr_group((struct qdf_mac_addr *)wh->i_addr1) && + !qdf_is_macaddr_broadcast((struct qdf_mac_addr *)wh->i_addr1)) { + + frame_len = *size + IEEE80211_CCMP_HEADERLEN + + IEEE80211_CCMP_MICLEN; + status = p2p_packet_alloc((uint16_t)frame_len, (void **)&frame, + &pkt); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("Failed to allocate %d bytes for rmf frame.", + frame_len); + return QDF_STATUS_E_NOMEM; + } + + qdf_mem_copy(frame, wh, sizeof(*wh)); + qdf_mem_copy(frame + sizeof(*wh) + IEEE80211_CCMP_HEADERLEN, + *ppbuf + sizeof(*wh), + *size - sizeof(*wh)); + rmf_wh = (struct wlan_frame_hdr *)frame; + (rmf_wh)->i_fc[1] |= IEEE80211_FC1_WEP; + p2p_debug("set protection 0x%x cat %d "QDF_MAC_ADDR_STR, + tx_ctx->frame_info.sub_type, + action_hdr->action_category, + QDF_MAC_ADDR_ARRAY(wh->i_addr1)); + + qdf_nbuf_free(*ppkt); + *ppbuf = frame; + *ppkt = pkt; + *size = frame_len; + } + + return status; +} + +/** + * p2p_execute_tx_action_frame() - execute tx action frame + * @tx_ctx: tx context + * + * This function modify p2p ie and tx this action frame. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_execute_tx_action_frame( + struct tx_action_context *tx_ctx) +{ + uint8_t *frame; + qdf_nbuf_t packet; + QDF_STATUS status; + uint8_t noa_len = 0; + uint8_t noa_stream[P2P_NOA_STREAM_ARR_SIZE]; + uint8_t orig_len = 0; + const uint8_t *ie; + uint8_t ie_len; + uint8_t *p2p_ie = NULL; + const uint8_t *presence_noa_attr = NULL; + uint32_t nbytes_copy; + uint32_t buf_len = tx_ctx->buf_len; + struct p2p_frame_info *frame_info; + + frame_info = &(tx_ctx->frame_info); + if (frame_info->sub_type == P2P_MGMT_PROBE_RSP) { + p2p_ie = (uint8_t *)p2p_get_p2pie_from_probe_rsp(tx_ctx); + } else if (frame_info->action_type == + P2P_ACTION_PRESENCE_RSP) { + ie = tx_ctx->buf + + P2P_PUBLIC_ACTION_FRAME_TYPE_OFFSET; + ie_len = tx_ctx->buf_len - + P2P_PUBLIC_ACTION_FRAME_TYPE_OFFSET; + p2p_ie = (uint8_t *)p2p_get_p2pie_ptr(ie, ie_len); + if (p2p_ie) { + /* extract the presence of NoA attribute inside + * P2P IE */ + ie = p2p_ie + P2P_IE_HEADER_LEN; + ie_len = p2p_ie[1]; + presence_noa_attr = p2p_get_presence_noa_attr( + ie, ie_len); + } + } + + if ((frame_info->sub_type != P2P_MGMT_NOT_SUPPORT) && + p2p_ie) { + orig_len = p2p_ie[1]; + noa_len = p2p_update_noa_stream(tx_ctx, p2p_ie, + presence_noa_attr, &buf_len, + noa_stream); + buf_len += noa_len; + } + + if (frame_info->sub_type == P2P_MGMT_PROBE_RSP) + p2p_set_ht_caps(tx_ctx, buf_len); + + /* Ok-- try to allocate some memory: */ + status = p2p_packet_alloc((uint16_t) buf_len, (void **)&frame, + &packet); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("Failed to allocate %d bytes for a Probe Request.", + buf_len); + return status; + } + + /* + * Add sequence number to action frames + * Frames are handed over in .11 format by supplicant already + */ + p2p_populate_mac_header(tx_ctx); + + if ((noa_len > 0) && p2p_ie + && (noa_len < (P2P_MAX_NOA_ATTR_LEN + + P2P_IE_HEADER_LEN))) { + /* Add 2 bytes for length and Arribute field */ + nbytes_copy = (p2p_ie + orig_len + 2) - tx_ctx->buf; + qdf_mem_copy(frame, tx_ctx->buf, nbytes_copy); + qdf_mem_copy((frame + nbytes_copy), noa_stream, + noa_len); + qdf_mem_copy((frame + nbytes_copy + noa_len), + tx_ctx->buf + nbytes_copy, + buf_len - nbytes_copy - noa_len); + } else { + qdf_mem_copy(frame, tx_ctx->buf, buf_len); + } + + status = p2p_populate_rmf_field(tx_ctx, &buf_len, &frame, &packet); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("failed to populate rmf frame"); + qdf_nbuf_free(packet); + return status; + } + status = p2p_mgmt_tx(tx_ctx, buf_len, packet, frame); + if (status == QDF_STATUS_SUCCESS) { + if (tx_ctx->no_ack) { + p2p_send_tx_conf(tx_ctx, true); + p2p_remove_tx_context(tx_ctx); + } else { + p2p_enable_tx_timer(tx_ctx); + p2p_move_tx_context_to_ack_queue(tx_ctx); + } + } else { + p2p_err("failed to tx mgmt frame"); + qdf_nbuf_free(packet); + } + + return status; +} + +struct tx_action_context *p2p_find_tx_ctx_by_nbuf( + struct p2p_soc_priv_obj *p2p_soc_obj, void *nbuf) +{ + struct tx_action_context *cur_tx_ctx; + qdf_list_node_t *p_node; + QDF_STATUS status; + + if (!p2p_soc_obj) { + p2p_err("invalid p2p soc object"); + return NULL; + } + + status = qdf_list_peek_front(&p2p_soc_obj->tx_q_ack, &p_node); + while (QDF_IS_STATUS_SUCCESS(status)) { + cur_tx_ctx = + qdf_container_of(p_node, struct tx_action_context, node); + if (cur_tx_ctx->nbuf == nbuf) { + p2p_debug("find tx ctx, nbuf:%pK", nbuf); + status = qdf_mc_timer_stop(&cur_tx_ctx->tx_timer); + if (status != QDF_STATUS_SUCCESS) + p2p_err("Failed to stop tx timer, status:%d", + status); + return cur_tx_ctx; + } + status = qdf_list_peek_next(&p2p_soc_obj->tx_q_ack, + p_node, &p_node); + } + + return NULL; +} + +void p2p_dump_tx_queue(struct p2p_soc_priv_obj *p2p_soc_obj) +{ + struct tx_action_context *tx_ctx; + qdf_list_node_t *p_node; + QDF_STATUS status; + + p2p_debug("dump tx queue wait for roc, p2p soc obj:%pK, size:%d", + p2p_soc_obj, qdf_list_size(&p2p_soc_obj->tx_q_roc)); + + status = qdf_list_peek_front(&p2p_soc_obj->tx_q_roc, &p_node); + while (QDF_IS_STATUS_SUCCESS(status)) { + tx_ctx = qdf_container_of(p_node, + struct tx_action_context, node); + p2p_debug("p2p soc object:%pK, tx ctx:%pK, vdev_id:%d, scan_id:%d, roc_cookie:%llx, chan:%d, buf:%pK, len:%d, off_chan:%d, cck:%d, ack:%d, duration:%d", + p2p_soc_obj, tx_ctx, + tx_ctx->vdev_id, tx_ctx->scan_id, + tx_ctx->roc_cookie, tx_ctx->chan, + tx_ctx->buf, tx_ctx->buf_len, + tx_ctx->off_chan, tx_ctx->no_cck, + tx_ctx->no_ack, tx_ctx->duration); + + status = qdf_list_peek_next(&p2p_soc_obj->tx_q_roc, + p_node, &p_node); + } + + p2p_debug("dump tx queue wait for ack, size:%d", + qdf_list_size(&p2p_soc_obj->tx_q_ack)); + status = qdf_list_peek_front(&p2p_soc_obj->tx_q_ack, &p_node); + while (QDF_IS_STATUS_SUCCESS(status)) { + tx_ctx = qdf_container_of(p_node, + struct tx_action_context, node); + p2p_debug("p2p soc object:%pK, tx_ctx:%pK, vdev_id:%d, scan_id:%d, roc_cookie:%llx, chan:%d, buf:%pK, len:%d, off_chan:%d, cck:%d, ack:%d, duration:%d", + p2p_soc_obj, tx_ctx, + tx_ctx->vdev_id, tx_ctx->scan_id, + tx_ctx->roc_cookie, tx_ctx->chan, + tx_ctx->buf, tx_ctx->buf_len, + tx_ctx->off_chan, tx_ctx->no_cck, + tx_ctx->no_ack, tx_ctx->duration); + + status = qdf_list_peek_next(&p2p_soc_obj->tx_q_ack, + p_node, &p_node); + } +} + +QDF_STATUS p2p_ready_to_tx_frame(struct p2p_soc_priv_obj *p2p_soc_obj, + uint64_t cookie) +{ + struct tx_action_context *cur_tx_ctx; + QDF_STATUS status = QDF_STATUS_SUCCESS; + + cur_tx_ctx = p2p_find_tx_ctx_by_roc(p2p_soc_obj, cookie); + + while (cur_tx_ctx) { + p2p_debug("tx_ctx:%pK", cur_tx_ctx); + status = p2p_execute_tx_action_frame(cur_tx_ctx); + if (status != QDF_STATUS_SUCCESS) { + p2p_send_tx_conf(cur_tx_ctx, false); + p2p_remove_tx_context(cur_tx_ctx); + } + cur_tx_ctx = p2p_find_tx_ctx_by_roc(p2p_soc_obj, cookie); + } + + return status; +} + +QDF_STATUS p2p_cleanup_tx_sync( + struct p2p_soc_priv_obj *p2p_soc_obj, + struct wlan_objmgr_vdev *vdev) +{ + struct scheduler_msg msg = {0}; + struct p2p_cleanup_param *param; + QDF_STATUS status; + uint32_t vdev_id; + + if (!p2p_soc_obj) { + p2p_err("p2p soc context is NULL"); + return QDF_STATUS_E_FAILURE; + } + + p2p_debug("p2p_soc_obj:%pK, vdev:%pK", p2p_soc_obj, vdev); + param = qdf_mem_malloc(sizeof(*param)); + if (!param) { + p2p_err("failed to allocate cleanup param"); + return QDF_STATUS_E_NOMEM; + } + + param->p2p_soc_obj = p2p_soc_obj; + if (vdev) + vdev_id = (uint32_t)wlan_vdev_get_id(vdev); + else + vdev_id = P2P_INVALID_VDEV_ID; + param->vdev_id = vdev_id; + qdf_event_reset(&p2p_soc_obj->cleanup_tx_done); + msg.type = P2P_CLEANUP_TX; + msg.bodyptr = param; + msg.callback = p2p_process_cmd; + status = scheduler_post_message(QDF_MODULE_ID_P2P, + QDF_MODULE_ID_P2P, + QDF_MODULE_ID_OS_IF, &msg); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("failed to post message"); + qdf_mem_free(param); + return status; + } + + status = qdf_wait_single_event( + &p2p_soc_obj->cleanup_tx_done, + P2P_WAIT_CLEANUP_ROC); + + if (status != QDF_STATUS_SUCCESS) + p2p_err("wait for cleanup tx timeout, %d", status); + + return status; +} + +QDF_STATUS p2p_process_cleanup_tx_queue(struct p2p_cleanup_param *param) +{ + struct tx_action_context *curr_tx_ctx; + qdf_list_node_t *p_node; + struct p2p_soc_priv_obj *p2p_soc_obj; + uint32_t vdev_id; + QDF_STATUS status, ret; + + if (!param || !(param->p2p_soc_obj)) { + p2p_err("Invalid cleanup param"); + return QDF_STATUS_E_FAILURE; + } + + p2p_soc_obj = param->p2p_soc_obj; + vdev_id = param->vdev_id; + + p2p_debug("clean up tx queue wait for roc, size:%d, vdev_id:%d", + qdf_list_size(&p2p_soc_obj->tx_q_roc), vdev_id); + + status = qdf_list_peek_front(&p2p_soc_obj->tx_q_roc, &p_node); + while (QDF_IS_STATUS_SUCCESS(status)) { + curr_tx_ctx = qdf_container_of(p_node, + struct tx_action_context, node); + status = qdf_list_peek_next(&p2p_soc_obj->tx_q_roc, + p_node, &p_node); + if ((vdev_id == P2P_INVALID_VDEV_ID) || + (vdev_id == curr_tx_ctx->vdev_id)) { + ret = qdf_list_remove_node(&p2p_soc_obj->tx_q_roc, + &curr_tx_ctx->node); + if (ret == QDF_STATUS_SUCCESS) { + p2p_send_tx_conf(curr_tx_ctx, false); + qdf_mem_free(curr_tx_ctx->buf); + qdf_mem_free(curr_tx_ctx); + } else + p2p_err("remove %pK from roc_q fail", + curr_tx_ctx); + } + } + + p2p_debug("clean up tx queue wait for ack, size:%d", + qdf_list_size(&p2p_soc_obj->tx_q_ack)); + + status = qdf_list_peek_front(&p2p_soc_obj->tx_q_ack, &p_node); + while (QDF_IS_STATUS_SUCCESS(status)) { + curr_tx_ctx = qdf_container_of(p_node, + struct tx_action_context, node); + status = qdf_list_peek_next(&p2p_soc_obj->tx_q_ack, + p_node, &p_node); + if ((vdev_id == P2P_INVALID_VDEV_ID) || + (vdev_id == curr_tx_ctx->vdev_id)) { + ret = qdf_list_remove_node(&p2p_soc_obj->tx_q_ack, + &curr_tx_ctx->node); + if (ret == QDF_STATUS_SUCCESS) { + p2p_disable_tx_timer(curr_tx_ctx); + p2p_send_tx_conf(curr_tx_ctx, false); + qdf_mem_free(curr_tx_ctx->buf); + qdf_mem_free(curr_tx_ctx); + } else + p2p_err("remove %pK from roc_q fail", + curr_tx_ctx); + } + } + + qdf_event_set(&p2p_soc_obj->cleanup_tx_done); + + return QDF_STATUS_SUCCESS; +} + +bool p2p_check_random_mac(struct wlan_objmgr_psoc *soc, uint32_t vdev_id, + uint8_t *random_mac_addr) +{ + uint32_t i = 0; + struct p2p_vdev_priv_obj *p2p_vdev_obj; + struct wlan_objmgr_vdev *vdev; + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(soc, vdev_id, WLAN_P2P_ID); + if (!vdev) { + p2p_debug("vdev is null"); + return false; + } + + p2p_vdev_obj = wlan_objmgr_vdev_get_comp_private_obj( + vdev, WLAN_UMAC_COMP_P2P); + if (!p2p_vdev_obj) { + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); + p2p_debug("p2p vdev object is NULL"); + return false; + } + + qdf_spin_lock(&p2p_vdev_obj->random_mac_lock); + for (i = 0; i < MAX_RANDOM_MAC_ADDRS; i++) { + if ((p2p_vdev_obj->random_mac[i].in_use) && + (!qdf_mem_cmp(p2p_vdev_obj->random_mac[i].addr, + random_mac_addr, QDF_MAC_ADDR_SIZE))) { + qdf_spin_unlock(&p2p_vdev_obj->random_mac_lock); + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); + return true; + } + } + qdf_spin_unlock(&p2p_vdev_obj->random_mac_lock); + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); + + return false; +} + +/** + * find_action_frame_cookie() - Checks for action cookie in cookie list + * @cookie_list: List of cookies + * @rnd_cookie: Cookie to be searched + * + * Return: If search is successful return pointer to action_frame_cookie + * object in which cookie item is encapsulated. + */ +static struct action_frame_cookie * +find_action_frame_cookie(qdf_list_t *cookie_list, uint64_t rnd_cookie) +{ + struct action_frame_cookie *action_cookie; + + qdf_list_for_each(cookie_list, action_cookie, cookie_node) { + if (action_cookie->cookie == rnd_cookie) + return action_cookie; + } + + return NULL; +} + +/** + * allocate_action_frame_cookie() - Allocate and add action cookie to + * given list + * @cookie_list: List of cookies + * @rnd_cookie: Cookie to be added + * + * Return: If allocation and addition is successful return pointer to + * action_frame_cookie object in which cookie item is encapsulated. + */ +static struct action_frame_cookie * +allocate_action_frame_cookie(qdf_list_t *cookie_list, uint64_t rnd_cookie) +{ + struct action_frame_cookie *action_cookie; + + action_cookie = qdf_mem_malloc(sizeof(*action_cookie)); + if (!action_cookie) + return NULL; + + action_cookie->cookie = rnd_cookie; + qdf_list_insert_front(cookie_list, &action_cookie->cookie_node); + + return action_cookie; +} + +/** + * delete_action_frame_cookie() - Delete the cookie from given list + * @cookie_list: List of cookies + * @action_cookie: Cookie to be deleted + * + * This function deletes the cookie item from given list and corresponding + * object in which it is encapsulated. + * + * Return: None + */ +static void +delete_action_frame_cookie(qdf_list_t *cookie_list, + struct action_frame_cookie *action_cookie) +{ + qdf_list_remove_node(cookie_list, &action_cookie->cookie_node); + qdf_mem_free(action_cookie); +} + +/** + * append_action_frame_cookie() - Append action cookie to given list + * @cookie_list: List of cookies + * @rnd_cookie: Cookie to be append + * + * This is a wrapper function which invokes allocate_action_frame_cookie + * if the cookie to be added is not duplicate + * + * Return: true - for successful case + * false - failed. + */ +static bool +append_action_frame_cookie(qdf_list_t *cookie_list, uint64_t rnd_cookie) +{ + struct action_frame_cookie *action_cookie; + + /* + * There should be no mac entry with empty cookie list, + * check and ignore if duplicate + */ + action_cookie = find_action_frame_cookie(cookie_list, rnd_cookie); + if (action_cookie) + /* random mac address is already programmed */ + return true; + + /* insert new cookie in cookie list */ + action_cookie = allocate_action_frame_cookie(cookie_list, rnd_cookie); + if (!action_cookie) + return false; + + return true; +} + +/** + * p2p_add_random_mac() - add or append random mac to given vdev rand mac list + * @soc: soc object + * @vdev_id: vdev id + * @mac: mac addr to be added or append + * @freq: frequency + * @rnd_cookie: random mac mgmt tx cookie + * + * This function will add or append the mac addr entry to vdev random mac list. + * Once the mac addr filter is not needed, it can be removed by + * p2p_del_random_mac. + * + * Return: QDF_STATUS_E_EXISTS - append to existing list + * QDF_STATUS_SUCCESS - add a new entry. + * other : failed to add the mac address entry. + */ +static QDF_STATUS +p2p_add_random_mac(struct wlan_objmgr_psoc *soc, uint32_t vdev_id, + uint8_t *mac, uint32_t freq, uint64_t rnd_cookie) +{ + uint32_t i; + uint32_t first_unused = MAX_RANDOM_MAC_ADDRS; + struct action_frame_cookie *action_cookie; + int32_t append_ret; + struct p2p_vdev_priv_obj *p2p_vdev_obj; + struct wlan_objmgr_vdev *vdev; + + p2p_debug("random_mac:vdev %d mac_addr:%pM rnd_cookie=%llu freq = %u", + vdev_id, mac, rnd_cookie, freq); + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(soc, vdev_id, WLAN_P2P_ID); + if (!vdev) { + p2p_debug("vdev is null"); + + return QDF_STATUS_E_INVAL; + } + + p2p_vdev_obj = wlan_objmgr_vdev_get_comp_private_obj( + vdev, WLAN_UMAC_COMP_P2P); + if (!p2p_vdev_obj) { + p2p_debug("random_mac:p2p vdev object is NULL"); + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); + + return QDF_STATUS_E_INVAL; + } + + qdf_spin_lock(&p2p_vdev_obj->random_mac_lock); + /* + * Following loop checks whether random mac entry is already + * present, if present get the index of matched entry else + * get the first unused slot to store this new random mac + */ + for (i = 0; i < MAX_RANDOM_MAC_ADDRS; i++) { + if (!p2p_vdev_obj->random_mac[i].in_use) { + if (first_unused == MAX_RANDOM_MAC_ADDRS) + first_unused = i; + continue; + } + + if (!qdf_mem_cmp(p2p_vdev_obj->random_mac[i].addr, mac, + QDF_MAC_ADDR_SIZE)) + break; + } + + if (i != MAX_RANDOM_MAC_ADDRS) { + append_ret = append_action_frame_cookie( + &p2p_vdev_obj->random_mac[i].cookie_list, + rnd_cookie); + qdf_spin_unlock(&p2p_vdev_obj->random_mac_lock); + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); + p2p_debug("random_mac:append %d vdev %d freq %d %pM rnd_cookie %llu", + append_ret, vdev_id, freq, mac, rnd_cookie); + if (!append_ret) { + p2p_debug("random_mac:failed to append rnd_cookie"); + return QDF_STATUS_E_NOMEM; + } + + return QDF_STATUS_E_EXISTS; + } + + if (first_unused == MAX_RANDOM_MAC_ADDRS) { + qdf_spin_unlock(&p2p_vdev_obj->random_mac_lock); + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); + p2p_debug("random_mac:Reached the limit of Max random addresses"); + + return QDF_STATUS_E_RESOURCES; + } + + /* get the first unused buf and store new random mac */ + i = first_unused; + + action_cookie = allocate_action_frame_cookie( + &p2p_vdev_obj->random_mac[i].cookie_list, + rnd_cookie); + if (!action_cookie) { + qdf_spin_unlock(&p2p_vdev_obj->random_mac_lock); + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); + p2p_err("random_mac:failed to alloc rnd cookie"); + + return QDF_STATUS_E_NOMEM; + } + qdf_mem_copy(p2p_vdev_obj->random_mac[i].addr, mac, QDF_MAC_ADDR_SIZE); + p2p_vdev_obj->random_mac[i].in_use = true; + p2p_vdev_obj->random_mac[i].freq = freq; + qdf_spin_unlock(&p2p_vdev_obj->random_mac_lock); + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); + p2p_debug("random_mac:add vdev %d freq %d %pM rnd_cookie %llu", + vdev_id, freq, mac, rnd_cookie); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +p2p_del_random_mac(struct wlan_objmgr_psoc *soc, uint32_t vdev_id, + uint64_t rnd_cookie, uint32_t duration) +{ + uint32_t i; + struct action_frame_cookie *action_cookie; + struct p2p_vdev_priv_obj *p2p_vdev_obj; + struct wlan_objmgr_vdev *vdev; + + p2p_debug("random_mac:vdev %d cookie %llu duration %d", vdev_id, + rnd_cookie, duration); + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(soc, vdev_id, + WLAN_P2P_ID); + if (!vdev) { + p2p_debug("vdev is null"); + return QDF_STATUS_E_INVAL; + } + p2p_vdev_obj = wlan_objmgr_vdev_get_comp_private_obj( + vdev, WLAN_UMAC_COMP_P2P); + if (!p2p_vdev_obj) { + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); + p2p_debug("p2p vdev object is NULL"); + return QDF_STATUS_E_INVAL; + } + + qdf_spin_lock(&p2p_vdev_obj->random_mac_lock); + for (i = 0; i < MAX_RANDOM_MAC_ADDRS; i++) { + struct action_frame_random_mac *random_mac; + + random_mac = &p2p_vdev_obj->random_mac[i]; + if (!random_mac->in_use) + continue; + + action_cookie = find_action_frame_cookie( + &random_mac->cookie_list, rnd_cookie); + if (!action_cookie) + continue; + + delete_action_frame_cookie( + &random_mac->cookie_list, + action_cookie); + + if (qdf_list_empty(&random_mac->cookie_list)) { + qdf_spin_unlock(&p2p_vdev_obj->random_mac_lock); + if (qdf_mc_timer_get_current_state( + &random_mac->clear_timer) == + QDF_TIMER_STATE_RUNNING) + qdf_mc_timer_stop(&random_mac->clear_timer); + qdf_mc_timer_start(&random_mac->clear_timer, duration); + + qdf_spin_lock(&p2p_vdev_obj->random_mac_lock); + p2p_debug("random_mac:noref on vdev %d addr %pM", + vdev_id, random_mac->addr); + } + break; + } + qdf_spin_unlock(&p2p_vdev_obj->random_mac_lock); + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); + + return QDF_STATUS_SUCCESS; +} + +void p2p_del_all_rand_mac_vdev(struct wlan_objmgr_vdev *vdev) +{ + int32_t i; + uint32_t freq; + uint8_t addr[QDF_MAC_ADDR_SIZE]; + struct p2p_vdev_priv_obj *p2p_vdev_obj; + + if (!vdev) + return; + p2p_vdev_obj = wlan_objmgr_vdev_get_comp_private_obj( + vdev, WLAN_UMAC_COMP_P2P); + if (!p2p_vdev_obj) + return; + + qdf_spin_lock(&p2p_vdev_obj->random_mac_lock); + for (i = 0; i < MAX_RANDOM_MAC_ADDRS; i++) { + struct action_frame_cookie *action_cookie; + struct action_frame_cookie *action_cookie_next; + + if (!p2p_vdev_obj->random_mac[i].in_use) + continue; + + /* empty the list and clear random addr */ + qdf_list_for_each_del(&p2p_vdev_obj->random_mac[i].cookie_list, + action_cookie, action_cookie_next, + cookie_node) { + qdf_list_remove_node( + &p2p_vdev_obj->random_mac[i].cookie_list, + &action_cookie->cookie_node); + qdf_mem_free(action_cookie); + } + + p2p_vdev_obj->random_mac[i].in_use = false; + freq = p2p_vdev_obj->random_mac[i].freq; + qdf_mem_copy(addr, p2p_vdev_obj->random_mac[i].addr, + QDF_MAC_ADDR_SIZE); + qdf_spin_unlock(&p2p_vdev_obj->random_mac_lock); + qdf_mc_timer_stop(&p2p_vdev_obj->random_mac[i].clear_timer); + p2p_clear_mac_filter(wlan_vdev_get_psoc(vdev), + wlan_vdev_get_id(vdev), addr, freq); + p2p_debug("random_mac:delall vdev %d freq %d addr %pM", + wlan_vdev_get_id(vdev), freq, addr); + + qdf_spin_lock(&p2p_vdev_obj->random_mac_lock); + } + qdf_spin_unlock(&p2p_vdev_obj->random_mac_lock); +} + +static void +p2p_del_rand_mac_vdev_enum_handler(struct wlan_objmgr_psoc *psoc, + void *obj, void *arg) +{ + struct wlan_objmgr_vdev *vdev = obj; + + if (!vdev) { + p2p_err("random_mac:invalid vdev"); + return; + } + + if (!p2p_is_vdev_support_rand_mac(vdev)) + return; + + p2p_del_all_rand_mac_vdev(vdev); +} + +void p2p_del_all_rand_mac_soc(struct wlan_objmgr_psoc *soc) +{ + if (!soc) { + p2p_err("random_mac:soc object is NULL"); + return; + } + + wlan_objmgr_iterate_obj_list(soc, WLAN_VDEV_OP, + p2p_del_rand_mac_vdev_enum_handler, + NULL, 0, WLAN_P2P_ID); +} + +/** + * p2p_is_random_mac() - check mac addr is random mac for vdev + * @soc: soc object + * @vdev_id: vdev id + * @mac: mac addr to be added or append + * + * This function will check the source mac addr same as vdev's mac addr or not. + * If not same, then the source mac addr should be random mac addr. + * + * Return: true if mac is random mac, otherwise false + */ +static bool +p2p_is_random_mac(struct wlan_objmgr_psoc *soc, uint32_t vdev_id, uint8_t *mac) +{ + bool ret = false; + struct wlan_objmgr_vdev *vdev; + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(soc, vdev_id, WLAN_P2P_ID); + if (!vdev) { + p2p_debug("random_mac:vdev is null"); + return false; + } + + if (qdf_mem_cmp(wlan_vdev_mlme_get_macaddr(vdev), + mac, QDF_MAC_ADDR_SIZE)) + ret = true; + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); + + return ret; +} + +static void p2p_set_mac_filter_callback(bool result, void *context) +{ + struct osif_request *request; + struct random_mac_priv *priv; + + p2p_debug("random_mac:set random mac filter result %d", result); + request = osif_request_get(context); + if (!request) { + p2p_err("random_mac:invalid response"); + return; + } + + priv = osif_request_priv(request); + priv->result = result; + + osif_request_complete(request); + osif_request_put(request); +} + +QDF_STATUS p2p_process_set_rand_mac_rsp(struct p2p_mac_filter_rsp *resp) +{ + struct wlan_objmgr_psoc *soc; + struct p2p_vdev_priv_obj *p2p_vdev_obj; + struct wlan_objmgr_vdev *vdev; + + if (!resp || !resp->p2p_soc_obj || !resp->p2p_soc_obj->soc) { + p2p_debug("random_mac:set_filter_req is null"); + return QDF_STATUS_E_INVAL; + } + p2p_debug("random_mac:process rsp on vdev %d status %d", resp->vdev_id, + resp->status); + soc = resp->p2p_soc_obj->soc; + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(soc, resp->vdev_id, + WLAN_P2P_ID); + if (!vdev) { + p2p_debug("random_mac:vdev is null vdev %d", resp->vdev_id); + return QDF_STATUS_E_INVAL; + } + + p2p_vdev_obj = wlan_objmgr_vdev_get_comp_private_obj( + vdev, WLAN_UMAC_COMP_P2P); + if (!p2p_vdev_obj) { + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); + p2p_debug("random_mac:p2p_vdev_obj is null vdev %d", + resp->vdev_id); + return QDF_STATUS_E_INVAL; + } + if (!p2p_vdev_obj->pending_req.soc) { + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); + p2p_debug("random_mac:no pending set req for vdev %d", + resp->vdev_id); + return QDF_STATUS_E_INVAL; + } + + p2p_debug("random_mac:get pending req on vdev %d set %d mac filter %pM freq %d", + p2p_vdev_obj->pending_req.vdev_id, + p2p_vdev_obj->pending_req.set, p2p_vdev_obj->pending_req.mac, + p2p_vdev_obj->pending_req.freq); + if (p2p_vdev_obj->pending_req.cb) + p2p_vdev_obj->pending_req.cb( + !!resp->status, p2p_vdev_obj->pending_req.req_cookie); + + qdf_mem_zero(&p2p_vdev_obj->pending_req, + sizeof(p2p_vdev_obj->pending_req)); + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +p2p_process_set_rand_mac(struct p2p_set_mac_filter_req *set_filter_req) +{ + struct wlan_objmgr_psoc *soc; + struct wlan_lmac_if_p2p_tx_ops *p2p_ops; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + struct p2p_set_mac_filter param; + struct p2p_vdev_priv_obj *p2p_vdev_obj; + struct wlan_objmgr_vdev *vdev; + + if (!set_filter_req || !set_filter_req->soc) { + p2p_debug("random_mac:set_filter_req is null"); + return QDF_STATUS_E_INVAL; + } + p2p_debug("random_mac:vdev %d set %d mac filter %pM freq %d", + set_filter_req->vdev_id, set_filter_req->set, + set_filter_req->mac, set_filter_req->freq); + + soc = set_filter_req->soc; + vdev = wlan_objmgr_get_vdev_by_id_from_psoc( + soc, set_filter_req->vdev_id, WLAN_P2P_ID); + if (!vdev) { + p2p_debug("random_mac:vdev is null vdev %d", + set_filter_req->vdev_id); + goto get_vdev_failed; + } + p2p_vdev_obj = wlan_objmgr_vdev_get_comp_private_obj( + vdev, WLAN_UMAC_COMP_P2P); + if (!p2p_vdev_obj) { + p2p_debug("random_mac:p2p_vdev_obj is null vdev %d", + set_filter_req->vdev_id); + goto get_p2p_obj_failed; + } + if (p2p_vdev_obj->pending_req.soc) { + p2p_debug("random_mac:Busy on vdev %d set %d mac filter %pM freq %d", + p2p_vdev_obj->pending_req.vdev_id, + p2p_vdev_obj->pending_req.set, + p2p_vdev_obj->pending_req.mac, + p2p_vdev_obj->pending_req.freq); + goto get_p2p_obj_failed; + } + + p2p_ops = p2p_psoc_get_tx_ops(soc); + if (p2p_ops && p2p_ops->set_mac_addr_rx_filter_cmd) { + qdf_mem_zero(¶m, sizeof(param)); + param.vdev_id = set_filter_req->vdev_id; + qdf_mem_copy(param.mac, set_filter_req->mac, + QDF_MAC_ADDR_SIZE); + param.freq = set_filter_req->freq; + param.set = set_filter_req->set; + status = p2p_ops->set_mac_addr_rx_filter_cmd(soc, ¶m); + if (status == QDF_STATUS_SUCCESS && set_filter_req->set) + qdf_mem_copy(&p2p_vdev_obj->pending_req, + set_filter_req, sizeof(*set_filter_req)); + p2p_debug("random_mac:p2p set mac addr rx filter, status:%d", + status); + } + +get_p2p_obj_failed: + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); + +get_vdev_failed: + if (status != QDF_STATUS_SUCCESS && + set_filter_req->cb) + set_filter_req->cb(false, set_filter_req->req_cookie); + + return status; +} + +/** + * p2p_set_mac_filter() - send set mac addr filter cmd + * @soc: soc + * @vdev_id: vdev id + * @mac: mac addr + * @freq: freq + * @set: set or clear + * @cb: callback func to be called when the request completed. + * @req_cookie: cookie to be returned + * + * This function send set random mac addr filter command to p2p component + * msg core + * + * Return: QDF_STATUS_SUCCESS - if sent successfully. + * otherwise : failed. + */ +static QDF_STATUS +p2p_set_mac_filter(struct wlan_objmgr_psoc *soc, uint32_t vdev_id, + uint8_t *mac, uint32_t freq, bool set, + p2p_request_mgr_callback_t cb, void *req_cookie) +{ + struct p2p_set_mac_filter_req *set_filter_req; + struct scheduler_msg msg = {0}; + QDF_STATUS status; + + p2p_debug("random_mac:vdev %d freq %d set %d %pM", + vdev_id, freq, set, mac); + + set_filter_req = qdf_mem_malloc(sizeof(*set_filter_req)); + if (!set_filter_req) + return QDF_STATUS_E_NOMEM; + + set_filter_req->soc = soc; + set_filter_req->vdev_id = vdev_id; + set_filter_req->freq = freq; + qdf_mem_copy(set_filter_req->mac, mac, QDF_MAC_ADDR_SIZE); + set_filter_req->set = set; + set_filter_req->cb = cb; + set_filter_req->req_cookie = req_cookie; + + msg.type = P2P_SET_RANDOM_MAC; + msg.bodyptr = set_filter_req; + msg.callback = p2p_process_cmd; + status = scheduler_post_msg(QDF_MODULE_ID_OS_IF, &msg); + if (status != QDF_STATUS_SUCCESS) + qdf_mem_free(set_filter_req); + + return status; +} + +QDF_STATUS +p2p_clear_mac_filter(struct wlan_objmgr_psoc *soc, uint32_t vdev_id, + uint8_t *mac, uint32_t freq) +{ + return p2p_set_mac_filter(soc, vdev_id, mac, freq, false, NULL, NULL); +} + +bool +p2p_is_vdev_support_rand_mac(struct wlan_objmgr_vdev *vdev) +{ + enum QDF_OPMODE mode; + + mode = wlan_vdev_mlme_get_opmode(vdev); + if (mode == QDF_STA_MODE || + mode == QDF_P2P_CLIENT_MODE || + mode == QDF_P2P_DEVICE_MODE) + return true; + return false; +} + +/** + * p2p_is_vdev_support_rand_mac_by_id() - check vdev type support random mac + * mgmt tx or not + * @soc: soc obj + * @vdev_id: vdev id + * + * Return: true: support random mac mgmt tx + * false: not support random mac mgmt tx. + */ +static bool +p2p_is_vdev_support_rand_mac_by_id(struct wlan_objmgr_psoc *soc, + uint32_t vdev_id) +{ + struct wlan_objmgr_vdev *vdev; + bool ret = false; + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(soc, vdev_id, WLAN_P2P_ID); + if (!vdev) + return false; + ret = p2p_is_vdev_support_rand_mac(vdev); + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); + + return ret; +} + +/** + * p2p_set_rand_mac() - set random mac address rx filter + * @soc: soc + * @vdev_id: vdev id + * @mac: mac addr + * @freq: freq + * @rnd_cookie: cookie to be returned + * + * This function will post msg to p2p core to set random mac addr rx filter. + * It will wait the respone and return the result to caller. + * + * Return: true: set successfully + * false: failed + */ +static bool +p2p_set_rand_mac(struct wlan_objmgr_psoc *soc, uint32_t vdev_id, + uint8_t *mac, uint32_t freq, uint64_t rnd_cookie) +{ + bool ret = false; + int err; + QDF_STATUS status; + struct osif_request *request; + static const struct osif_request_params params = { + .priv_size = sizeof(struct random_mac_priv), + .timeout_ms = WLAN_WAIT_TIME_SET_RND, + }; + void *req_cookie; + struct random_mac_priv *priv; + + request = osif_request_alloc(¶ms); + if (!request) { + p2p_err("Request allocation failure"); + return false; + } + + req_cookie = osif_request_cookie(request); + + status = p2p_set_mac_filter(soc, vdev_id, mac, freq, true, + p2p_set_mac_filter_callback, req_cookie); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("random_mac:set mac fitler failure %d", status); + } else { + err = osif_request_wait_for_response(request); + if (err) { + p2p_err("random_mac:timeout for set mac fitler %d", + err); + } else { + priv = osif_request_priv(request); + ret = priv->result; + p2p_debug("random_mac:vdev %d freq %d result %d %pM rnd_cookie %llu", + vdev_id, freq, priv->result, mac, rnd_cookie); + } + } + osif_request_put(request); + + return ret; +} + +/** + * p2p_request_random_mac() - request random mac mgmt tx + * @soc: soc + * @vdev_id: vdev id + * @mac: mac addr + * @freq: freq + * @rnd_cookie: cookie to be returned + * @duration: duration of tx timeout + * + * This function will add/append the random mac addr filter entry to vdev. + * If it is new added entry, it will request to set filter in target. + * + * Return: QDF_STATUS_SUCCESS: request successfully + * other: failed + */ +static QDF_STATUS +p2p_request_random_mac(struct wlan_objmgr_psoc *soc, uint32_t vdev_id, + uint8_t *mac, uint32_t freq, uint64_t rnd_cookie, + uint32_t duration) +{ + QDF_STATUS status; + + status = p2p_add_random_mac(soc, vdev_id, mac, freq, rnd_cookie); + if (status == QDF_STATUS_E_EXISTS) + return QDF_STATUS_SUCCESS; + else if (status != QDF_STATUS_SUCCESS) + return status; + + if (!p2p_set_rand_mac(soc, vdev_id, mac, freq, rnd_cookie)) + status = p2p_del_random_mac(soc, vdev_id, rnd_cookie, + duration); + + return status; +} + +void p2p_rand_mac_tx(struct tx_action_context *tx_action) +{ + struct wlan_objmgr_psoc *soc; + QDF_STATUS status; + + if (!tx_action || !tx_action->p2p_soc_obj || + !tx_action->p2p_soc_obj->soc) + return; + soc = tx_action->p2p_soc_obj->soc; + + if (!tx_action->no_ack && tx_action->chan && + tx_action->buf_len > MIN_MAC_HEADER_LEN && + p2p_is_vdev_support_rand_mac_by_id(soc, tx_action->vdev_id) && + p2p_is_random_mac(soc, tx_action->vdev_id, + &tx_action->buf[SRC_MAC_ADDR_OFFSET])) { + status = p2p_request_random_mac( + soc, tx_action->vdev_id, + &tx_action->buf[SRC_MAC_ADDR_OFFSET], + wlan_chan_to_freq(tx_action->chan), + tx_action->id, + tx_action->duration); + if (status == QDF_STATUS_SUCCESS) + tx_action->rand_mac_tx = true; + else + tx_action->rand_mac_tx = false; + } +} + +void +p2p_rand_mac_tx_done(struct wlan_objmgr_psoc *soc, + struct tx_action_context *tx_ctx) +{ + if (!tx_ctx || !tx_ctx->rand_mac_tx || !soc) + return; + + p2p_del_random_mac(soc, tx_ctx->vdev_id, tx_ctx->id, tx_ctx->duration); +} + +/** + * p2p_mac_clear_timeout() - clear random mac filter timeout + * @context: timer context + * + * This function will clear the mac addr rx filter in target if no + * reference to it. + * + * Return: void + */ +static void p2p_mac_clear_timeout(void *context) +{ + struct action_frame_random_mac *random_mac = context; + struct p2p_vdev_priv_obj *p2p_vdev_obj; + uint32_t freq; + uint8_t addr[QDF_MAC_ADDR_SIZE]; + uint32_t vdev_id; + bool clear = false; + + if (!random_mac || !random_mac->p2p_vdev_obj) { + p2p_err("invalid context for mac_clear timeout"); + return; + } + p2p_vdev_obj = random_mac->p2p_vdev_obj; + if (!p2p_vdev_obj || !p2p_vdev_obj->vdev) + return; + + qdf_spin_lock(&p2p_vdev_obj->random_mac_lock); + if (qdf_list_empty(&random_mac->cookie_list)) { + random_mac->in_use = false; + clear = true; + } + freq = random_mac->freq; + qdf_mem_copy(addr, random_mac->addr, QDF_MAC_ADDR_SIZE); + qdf_spin_unlock(&p2p_vdev_obj->random_mac_lock); + + vdev_id = wlan_vdev_get_id(p2p_vdev_obj->vdev); + p2p_debug("random_mac:clear timeout vdev %d %pM freq %d clr %d", + vdev_id, addr, freq, clear); + if (clear) + p2p_clear_mac_filter(wlan_vdev_get_psoc(p2p_vdev_obj->vdev), + vdev_id, addr, freq); +} + +void p2p_init_random_mac_vdev(struct p2p_vdev_priv_obj *p2p_vdev_obj) +{ + int32_t i; + + qdf_spinlock_create(&p2p_vdev_obj->random_mac_lock); + for (i = 0; i < MAX_RANDOM_MAC_ADDRS; i++) { + qdf_mem_zero(&p2p_vdev_obj->random_mac[i], + sizeof(struct action_frame_random_mac)); + p2p_vdev_obj->random_mac[i].in_use = false; + p2p_vdev_obj->random_mac[i].p2p_vdev_obj = p2p_vdev_obj; + qdf_list_create(&p2p_vdev_obj->random_mac[i].cookie_list, 0); + qdf_mc_timer_init(&p2p_vdev_obj->random_mac[i].clear_timer, + QDF_TIMER_TYPE_SW, p2p_mac_clear_timeout, + &p2p_vdev_obj->random_mac[i]); + } +} + +void p2p_deinit_random_mac_vdev(struct p2p_vdev_priv_obj *p2p_vdev_obj) +{ + int32_t i; + + p2p_del_all_rand_mac_vdev(p2p_vdev_obj->vdev); + for (i = 0; i < MAX_RANDOM_MAC_ADDRS; i++) { + qdf_mc_timer_destroy(&p2p_vdev_obj->random_mac[i].clear_timer); + qdf_list_destroy(&p2p_vdev_obj->random_mac[i].cookie_list); + } + qdf_spinlock_destroy(&p2p_vdev_obj->random_mac_lock); +} + +QDF_STATUS p2p_process_mgmt_tx(struct tx_action_context *tx_ctx) +{ + struct p2p_soc_priv_obj *p2p_soc_obj; + struct p2p_roc_context *curr_roc_ctx; + uint8_t *mac_to; + QDF_STATUS status; + + status = p2p_tx_context_check_valid(tx_ctx); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("invalid tx action context"); + if (tx_ctx) { + if (tx_ctx->buf) { + p2p_send_tx_conf(tx_ctx, false); + qdf_mem_free(tx_ctx->buf); + } + qdf_mem_free(tx_ctx); + } + return QDF_STATUS_E_INVAL; + } + + p2p_soc_obj = tx_ctx->p2p_soc_obj; + + p2p_debug("soc:%pK, tx_ctx:%pK, vdev_id:%d, scan_id:%d, roc_cookie:%llx, chan:%d, buf:%pK, len:%d, off_chan:%d, cck:%d, ack:%d, duration:%d", + p2p_soc_obj->soc, tx_ctx, tx_ctx->vdev_id, + tx_ctx->scan_id, tx_ctx->roc_cookie, tx_ctx->chan, + tx_ctx->buf, tx_ctx->buf_len, tx_ctx->off_chan, + tx_ctx->no_cck, tx_ctx->no_ack, tx_ctx->duration); + + status = p2p_get_frame_info(tx_ctx->buf, tx_ctx->buf_len, + &(tx_ctx->frame_info)); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("unsupport frame"); + status = QDF_STATUS_E_INVAL; + goto fail; + } + + /* update P2P connection status with tx frame info */ + mac_to = &(tx_ctx->buf[DST_MAC_ADDR_OFFSET]); + p2p_tx_update_connection_status(p2p_soc_obj, + &(tx_ctx->frame_info), mac_to); + + status = p2p_vdev_check_valid(tx_ctx); + if (status != QDF_STATUS_SUCCESS) { + p2p_debug("invalid vdev or vdev mode"); + status = QDF_STATUS_E_INVAL; + goto fail; + } + + /* Do not wait for ack for probe response */ + if (tx_ctx->frame_info.sub_type == P2P_MGMT_PROBE_RSP && + !(tx_ctx->no_ack)) { + p2p_debug("Force set no ack to 1"); + tx_ctx->no_ack = 1; + } + + if (!tx_ctx->off_chan) { + status = p2p_execute_tx_action_frame(tx_ctx); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("execute tx fail"); + goto fail; + } else + return QDF_STATUS_SUCCESS; + } + + /* For off channel tx case */ + curr_roc_ctx = p2p_find_current_roc_ctx(p2p_soc_obj); + if (curr_roc_ctx && (curr_roc_ctx->chan == tx_ctx->chan)) { + if ((curr_roc_ctx->roc_state == ROC_STATE_REQUESTED) || + (curr_roc_ctx->roc_state == ROC_STATE_STARTED)) { + tx_ctx->roc_cookie = (uintptr_t)curr_roc_ctx; + status = qdf_list_insert_back( + &p2p_soc_obj->tx_q_roc, + &tx_ctx->node); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("Failed to insert off chan tx context to wait roc req queue"); + goto fail; + } else + return QDF_STATUS_SUCCESS; + } else if (curr_roc_ctx->roc_state == ROC_STATE_ON_CHAN) { + p2p_adjust_tx_wait(tx_ctx); + status = p2p_restart_roc_timer(curr_roc_ctx); + curr_roc_ctx->tx_ctx = tx_ctx; + if (status != QDF_STATUS_SUCCESS) { + p2p_err("restart roc timer fail"); + goto fail; + } + status = p2p_execute_tx_action_frame(tx_ctx); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("execute tx fail"); + goto fail; + } else + return QDF_STATUS_SUCCESS; + } + } + + curr_roc_ctx = p2p_find_roc_by_chan(p2p_soc_obj, tx_ctx->chan); + if (curr_roc_ctx && (curr_roc_ctx->roc_state == ROC_STATE_IDLE)) { + tx_ctx->roc_cookie = (uintptr_t)curr_roc_ctx; + status = qdf_list_insert_back( + &p2p_soc_obj->tx_q_roc, + &tx_ctx->node); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("Failed to insert off chan tx context to wait roc req queue"); + goto fail; + } else { + return QDF_STATUS_SUCCESS; + } + } + + status = p2p_roc_req_for_tx_action(tx_ctx); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("Failed to request roc before off chan tx"); + goto fail; + } + + return QDF_STATUS_SUCCESS; + +fail: + p2p_send_tx_conf(tx_ctx, false); + qdf_idr_remove(&p2p_soc_obj->p2p_idr, tx_ctx->id); + qdf_mem_free(tx_ctx->buf); + qdf_mem_free(tx_ctx); + + return status; +} + +QDF_STATUS p2p_process_mgmt_tx_cancel( + struct cancel_roc_context *cancel_tx) +{ + bool is_roc_q = false; + bool is_ack_q = false; + struct tx_action_context *cur_tx_ctx; + struct p2p_roc_context *cur_roc_ctx; + struct cancel_roc_context cancel_roc; + + if (!cancel_tx || !(cancel_tx->cookie)) { + p2p_info("invalid cancel info"); + return QDF_STATUS_SUCCESS; + } + + p2p_debug("cookie:0x%llx", cancel_tx->cookie); + + cur_tx_ctx = p2p_find_tx_ctx(cancel_tx->p2p_soc_obj, + cancel_tx->cookie, &is_roc_q, &is_ack_q); + if (cur_tx_ctx) { + if (is_roc_q) { + cancel_roc.p2p_soc_obj = + cancel_tx->p2p_soc_obj; + cancel_roc.cookie = + cur_tx_ctx->roc_cookie; + return p2p_process_cancel_roc_req(&cancel_roc); + } + if (is_ack_q) { + /*Has tx action frame, waiting for ack*/ + p2p_debug("Waiting for ack, cookie %llx", + cancel_tx->cookie); + } + } else { + p2p_debug("Failed to find tx ctx by cookie, cookie %llx", + cancel_tx->cookie); + + cur_roc_ctx = p2p_find_roc_by_tx_ctx(cancel_tx->p2p_soc_obj, + cancel_tx->cookie); + if (cur_roc_ctx) { + p2p_debug("tx ctx:%llx, roc:%pK", + cancel_tx->cookie, cur_roc_ctx); + cancel_roc.p2p_soc_obj = + cancel_tx->p2p_soc_obj; + cancel_roc.cookie = (uintptr_t) cur_roc_ctx; + return p2p_process_cancel_roc_req(&cancel_roc); + } + + p2p_debug("Failed to find roc by tx ctx"); + return QDF_STATUS_E_INVAL; + } + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS p2p_process_mgmt_tx_ack_cnf( + struct p2p_tx_conf_event *tx_cnf_event) +{ + struct p2p_tx_cnf tx_cnf; + struct tx_action_context *tx_ctx; + struct p2p_soc_priv_obj *p2p_soc_obj; + struct p2p_start_param *start_param; + + p2p_soc_obj = tx_cnf_event->p2p_soc_obj; + + if (!p2p_soc_obj || !(p2p_soc_obj->start_param)) { + qdf_nbuf_free(tx_cnf_event->nbuf); + p2p_err("Invalid p2p soc object or start parameters"); + return QDF_STATUS_E_INVAL; + } + + tx_ctx = p2p_find_tx_ctx_by_nbuf(p2p_soc_obj, tx_cnf_event->nbuf); + qdf_nbuf_free(tx_cnf_event->nbuf); + if (!tx_ctx) { + p2p_err("can't find tx_ctx, tx ack comes late"); + return QDF_STATUS_SUCCESS; + } + + tx_cnf.vdev_id = tx_ctx->vdev_id; + tx_cnf.action_cookie = (uint64_t)tx_ctx->id; + tx_cnf.buf = tx_ctx->buf; + tx_cnf.buf_len = tx_ctx->buf_len; + tx_cnf.status = tx_cnf_event->status; + + p2p_debug("soc:%pK, vdev_id:%d, action_cookie:%llx, len:%d, status:%d, buf:%pK", + p2p_soc_obj->soc, tx_cnf.vdev_id, + tx_cnf.action_cookie, tx_cnf.buf_len, + tx_cnf.status, tx_cnf.buf); + + p2p_rand_mac_tx_done(p2p_soc_obj->soc, tx_ctx); + + /* disable tx timer */ + p2p_disable_tx_timer(tx_ctx); + + start_param = p2p_soc_obj->start_param; + if (start_param->tx_cnf_cb) + start_param->tx_cnf_cb(start_param->tx_cnf_cb_data, + &tx_cnf); + else + p2p_debug("Got tx conf, but no valid up layer callback"); + + p2p_remove_tx_context(tx_ctx); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS p2p_process_rx_mgmt( + struct p2p_rx_mgmt_event *rx_mgmt_event) +{ + struct p2p_rx_mgmt_frame *rx_mgmt; + struct p2p_soc_priv_obj *p2p_soc_obj; + struct p2p_start_param *start_param; + struct p2p_frame_info frame_info; + uint8_t *mac_from; + + p2p_soc_obj = rx_mgmt_event->p2p_soc_obj; + rx_mgmt = rx_mgmt_event->rx_mgmt; + + if (!p2p_soc_obj || !(p2p_soc_obj->start_param)) { + p2p_err("Invalid psoc object or start parameters"); + qdf_mem_free(rx_mgmt); + return QDF_STATUS_E_INVAL; + } + + p2p_debug("soc:%pK, frame_len:%d, rx_chan:%d, vdev_id:%d, frm_type:%d, rx_rssi:%d, buf:%pK", + p2p_soc_obj->soc, rx_mgmt->frame_len, + rx_mgmt->rx_chan, rx_mgmt->vdev_id, rx_mgmt->frm_type, + rx_mgmt->rx_rssi, rx_mgmt->buf); + + if (rx_mgmt->frm_type == MGMT_ACTION_VENDOR_SPECIFIC) { + p2p_get_frame_info(rx_mgmt->buf, rx_mgmt->frame_len, + &frame_info); + + /* update P2P connection status with rx frame info */ + mac_from = &(rx_mgmt->buf[SRC_MAC_ADDR_OFFSET]); + p2p_rx_update_connection_status(p2p_soc_obj, + &frame_info, mac_from); + + p2p_debug("action_sub_type %u, action_type %d", + frame_info.public_action_type, + frame_info.action_type); + + if ((frame_info.public_action_type == + P2P_PUBLIC_ACTION_NOT_SUPPORT) && + (frame_info.action_type == + P2P_ACTION_NOT_SUPPORT)) { + p2p_debug("non-p2p frame, drop it"); + qdf_mem_free(rx_mgmt); + return QDF_STATUS_SUCCESS; + } else { + p2p_debug("p2p frame, extend roc accordingly"); + p2p_extend_roc_timer(p2p_soc_obj, &frame_info); + } + } + + if (rx_mgmt->frm_type == MGMT_ACTION_CATEGORY_VENDOR_SPECIFIC) + p2p_get_frame_info(rx_mgmt->buf, rx_mgmt->frame_len, + &frame_info); + + start_param = p2p_soc_obj->start_param; + if (start_param->rx_cb) + start_param->rx_cb(start_param->rx_cb_data, rx_mgmt); + else + p2p_debug("rx mgmt, but no valid up layer callback"); + + qdf_mem_free(rx_mgmt); + + return QDF_STATUS_SUCCESS; +} diff --git a/p2p/core/src/wlan_p2p_off_chan_tx.h b/p2p/core/src/wlan_p2p_off_chan_tx.h new file mode 100644 index 0000000000..367ecd6325 --- /dev/null +++ b/p2p/core/src/wlan_p2p_off_chan_tx.h @@ -0,0 +1,439 @@ +/* + * Copyright (c) 2017-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. + */ + +/** + * DOC: Defines off channel tx API & structures + */ + +#ifndef _WLAN_P2P_OFF_CHAN_TX_H_ +#define _WLAN_P2P_OFF_CHAN_TX_H_ + +#include +#include +#include + +#define P2P_EID_VENDOR 0xdd +#define P2P_ACTION_VENDOR_SPECIFIC_CATEGORY 0x7F +#define P2P_PUBLIC_ACTION_FRAME 0x4 +#define P2P_MAC_MGMT_ACTION 0xD +#define P2P_PUBLIC_ACTION_VENDOR_SPECIFIC 0x9 +#define P2P_NOA_ATTR 0xC + +#define P2P_MAX_NOA_ATTR_LEN 31 +#define P2P_IE_HEADER_LEN 6 +#define P2P_MAX_IE_LENGTH 255 +#define P2P_ACTION_OFFSET 24 +#define P2P_PUBLIC_ACTION_FRAME_TYPE_OFFSET 30 +#define P2P_ACTION_FRAME_TYPE_OFFSET 29 +#define PROBE_RSP_IE_OFFSET 36 + +#define P2P_TX_PKT_MIN_HEADROOM (64) + +#define P2P_OUI "\x50\x6f\x9a\x09" +#define P2P_OUI_SIZE 4 + +#define P2P_ACTION_FRAME_RSP_WAIT 500 +#define P2P_ACTION_FRAME_ACK_WAIT 300 +#define P2P_ACTION_FRAME_TX_TIMEOUT 2000 + +#define DST_MAC_ADDR_OFFSET 4 +#define SRC_MAC_ADDR_OFFSET (DST_MAC_ADDR_OFFSET + QDF_MAC_ADDR_SIZE) + +#define P2P_NOA_STREAM_ARR_SIZE (P2P_MAX_NOA_ATTR_LEN + (2 * P2P_IE_HEADER_LEN)) + +#define P2P_GET_TYPE_FRM_FC(__fc__) (((__fc__) & 0x0F) >> 2) +#define P2P_GET_SUBTYPE_FRM_FC(__fc__) (((__fc__) & 0xF0) >> 4) + +#define WLAN_WAIT_TIME_SET_RND 100 + +struct p2p_soc_priv_obj; +struct cancel_roc_context; +struct p2p_tx_conf_event; +struct p2p_rx_mgmt_event; + +/** + * enum p2p_frame_type - frame type + * @P2P_FRAME_MGMT: mgmt frame + * @P2P_FRAME_NOT_SUPPORT: not support frame type + */ +enum p2p_frame_type { + P2P_FRAME_MGMT = 0, + P2P_FRAME_NOT_SUPPORT, +}; + +/** + * enum p2p_frame_sub_type - frame sub type + * @P2P_MGMT_PROBE_REQ: probe request frame + * @P2P_MGMT_PROBE_RSP: probe response frame + * @P2P_MGMT_ACTION: action frame + * @P2P_MGMT_NOT_SUPPORT: not support sub frame type + */ +enum p2p_frame_sub_type { + P2P_MGMT_PROBE_REQ = 4, + P2P_MGMT_PROBE_RSP, + P2P_MGMT_ACTION = 13, + P2P_MGMT_NOT_SUPPORT, +}; + +/** + * enum p2p_public_action_type - public action frame type + * @P2P_PUBLIC_ACTION_NEG_REQ: go negotiation request frame + * @P2P_PUBLIC_ACTION_NEG_RSP: go negotiation response frame + * @P2P_PUBLIC_ACTION_NEG_CNF: go negotiation confirm frame + * @P2P_PUBLIC_ACTION_INVIT_REQ: p2p invitation request frame + * @P2P_PUBLIC_ACTION_INVIT_RSP: p2p invitation response frame + * @P2P_PUBLIC_ACTION_DEV_DIS_REQ: device discoverability request + * @P2P_PUBLIC_ACTION_DEV_DIS_RSP: device discoverability response + * @P2P_PUBLIC_ACTION_PROV_DIS_REQ: provision discovery request + * @P2P_PUBLIC_ACTION_PROV_DIS_RSP: provision discovery response + * @P2P_PUBLIC_ACTION_GAS_INIT_REQ: gas initial request, + * @P2P_PUBLIC_ACTION_GAS_INIT_RSP: gas initial response + * @P2P_PUBLIC_ACTION_GAS_COMB_REQ: gas comeback request + * @P2P_PUBLIC_ACTION_GAS_COMB_RSP: gas comeback response + * @P2P_PUBLIC_ACTION_NOT_SUPPORT: not support p2p public action frame + */ +enum p2p_public_action_type { + P2P_PUBLIC_ACTION_NEG_REQ = 0, + P2P_PUBLIC_ACTION_NEG_RSP, + P2P_PUBLIC_ACTION_NEG_CNF, + P2P_PUBLIC_ACTION_INVIT_REQ, + P2P_PUBLIC_ACTION_INVIT_RSP, + P2P_PUBLIC_ACTION_DEV_DIS_REQ, + P2P_PUBLIC_ACTION_DEV_DIS_RSP, + P2P_PUBLIC_ACTION_PROV_DIS_REQ, + P2P_PUBLIC_ACTION_PROV_DIS_RSP, + P2P_PUBLIC_ACTION_GAS_INIT_REQ = 10, + P2P_PUBLIC_ACTION_GAS_INIT_RSP, + P2P_PUBLIC_ACTION_GAS_COMB_REQ, + P2P_PUBLIC_ACTION_GAS_COMB_RSP, + P2P_PUBLIC_ACTION_NOT_SUPPORT, +}; + +/** + * enum p2p_action_type - p2p action frame type + * @P2P_ACTION_PRESENCE_REQ: presence request frame + * @P2P_ACTION_PRESENCE_RSP: presence response frame + * @P2P_ACTION_NOT_SUPPORT: not support action frame type + */ +enum p2p_action_type { + P2P_ACTION_PRESENCE_REQ = 1, + P2P_ACTION_PRESENCE_RSP = 2, + P2P_ACTION_NOT_SUPPORT, +}; + +struct p2p_frame_info { + enum p2p_frame_type type; + enum p2p_frame_sub_type sub_type; + enum p2p_public_action_type public_action_type; + enum p2p_action_type action_type; +}; + +/** + * struct tx_action_context - tx action frame context + * @node: Node for next element in the list + * @p2p_soc_obj: Pointer to SoC global p2p private object + * @vdev_id: Vdev id on which this request has come + * @scan_id: Scan id given by scan component for this roc req + * @roc_cookie: Cookie for remain on channel request + * @id: Identifier of this tx context + * @chan: Chan for which this tx has been requested + * @buf: tx buffer + * @buf_len: Length of tx buffer + * @off_chan: Is this off channel tx + * @no_cck: Required cck or not + * @no_ack: Required ack or not + * @duration: Duration for the RoC + * @tx_timer: RoC timer + * @frame_info: Frame type information + */ +struct tx_action_context { + qdf_list_node_t node; + struct p2p_soc_priv_obj *p2p_soc_obj; + int vdev_id; + int scan_id; + uint64_t roc_cookie; + int32_t id; + uint8_t chan; + uint8_t *buf; + int buf_len; + bool off_chan; + bool no_cck; + bool no_ack; + bool rand_mac_tx; + uint32_t duration; + qdf_mc_timer_t tx_timer; + struct p2p_frame_info frame_info; + qdf_nbuf_t nbuf; +}; + +/** + * p2p_rand_mac_tx_done() - process random mac mgmt tx done + * @soc: soc + * @tx_ctx: tx context + * + * This function will remove the random mac addr filter reference. + * + * Return: void + */ +void +p2p_rand_mac_tx_done(struct wlan_objmgr_psoc *soc, + struct tx_action_context *tx_ctx); + +/** + * p2p_clear_mac_filter() - send clear mac addr filter cmd + * @soc: soc + * @vdev_id: vdev id + * @mac: mac addr + * @freq: freq + * + * This function send clear random mac addr filter command to p2p component + * msg core + * + * Return: QDF_STATUS_SUCCESS - if sent successfully. + * otherwise: failed. + */ +QDF_STATUS +p2p_clear_mac_filter(struct wlan_objmgr_psoc *soc, uint32_t vdev_id, + uint8_t *mac, uint32_t freq); + +/** + * p2p_is_vdev_support_rand_mac() - check vdev type support random mac mgmt + * tx or not + * @vdev: vdev object + * + * Return: true: support random mac mgmt tx + * false: not support random mac mgmt tx. + */ +bool +p2p_is_vdev_support_rand_mac(struct wlan_objmgr_vdev *vdev); + +/** + * p2p_dump_tx_queue() - dump tx queue + * @p2p_soc_obj: p2p soc private object + * + * This function dumps tx queue and output details about tx context in + * queue. + * + * Return: None + */ +void p2p_dump_tx_queue(struct p2p_soc_priv_obj *p2p_soc_obj); + +/** + * p2p_ready_to_tx_frame() - dump tx queue + * @p2p_soc_obj: p2p soc private object + * @cookie: cookie is pointer to roc + * + * This function find out the tx context in wait for roc queue and tx + * this frame. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_ready_to_tx_frame(struct p2p_soc_priv_obj *p2p_soc_obj, + uint64_t cookie); + +/** + * p2p_cleanup_tx_sync() - Cleanup tx queue + * @p2p_soc_obj: p2p psoc private object + * @vdev: vdev object + * + * This function cleanup tx context in queue until cancellation done. + * To avoid deadlock, don't call from scheduler thread. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_cleanup_tx_sync( + struct p2p_soc_priv_obj *p2p_soc_obj, + struct wlan_objmgr_vdev *vdev); + +/** + * p2p_process_cleanup_tx_queue() - process the message to cleanup tx + * @param: pointer to cleanup parameters + * + * This function cleanup wait for roc queue and wait for ack queue. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_process_cleanup_tx_queue( + struct p2p_cleanup_param *param); + +/** + * p2p_process_mgmt_tx() - Process mgmt frame tx request + * @tx_ctx: tx context + * + * This function handles mgmt frame tx request. It will call API from + * mgmt txrx component. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_process_mgmt_tx(struct tx_action_context *tx_ctx); + +/** + * p2p_process_mgmt_tx_cancel() - Process cancel mgmt frame tx request + * @cancel_tx: cancel tx context + * + * This function cancel mgmt frame tx request by cookie. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_process_mgmt_tx_cancel( + struct cancel_roc_context *cancel_tx); + +/** + * p2p_process_mgmt_tx_ack_cnf() - Process tx ack event + * @tx_cnf_event: tx confirmation event information + * + * This function mgmt frame tx confirmation. It will deliver this + * event to up layer + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_process_mgmt_tx_ack_cnf( + struct p2p_tx_conf_event *tx_cnf_event); + +/** + * p2p_process_rx_mgmt() - Process rx mgmt frame event + * @rx_mgmt_event: rx mgmt frame event information + * + * This function mgmt frame rx mgmt frame event. It will deliver this + * event to up layer + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_process_rx_mgmt( + struct p2p_rx_mgmt_event *rx_mgmt_event); + +/** + * p2p_find_tx_ctx_by_nbuf() - find tx context by nbuf + * @p2p_soc_obj: p2p soc object + * @nbuf: pointer to nbuf + * + * This function finds out tx context by nbuf. + * + * Return: pointer to tx context + */ +struct tx_action_context *p2p_find_tx_ctx_by_nbuf( + struct p2p_soc_priv_obj *p2p_soc_obj, void *nbuf); + +#define P2P_80211_FRM_SA_OFFSET 10 + +/** + * p2p_del_random_mac() - del mac fitler from given vdev rand mac list + * @soc: soc object + * @vdev_id: vdev id + * @rnd_cookie: random mac mgmt tx cookie + * @duration: timeout value to flush the addr in target. + * + * This function will del the mac addr filter from vdev random mac addr list. + * If there is no reference to mac addr, it will set a clear timer to flush it + * in target finally. + * + * Return: QDF_STATUS_SUCCESS - del successfully. + * other : failed to del the mac address entry. + */ +QDF_STATUS +p2p_del_random_mac(struct wlan_objmgr_psoc *soc, uint32_t vdev_id, + uint64_t rnd_cookie, uint32_t duration); + +/** + * p2p_check_random_mac() - check random mac addr or not + * @soc: soc context + * @vdev_id: vdev id + * @random_mac_addr: mac addr to be checked + * + * This function check the input addr is random mac addr or not for vdev. + * + * Return: true if addr is random mac address else false. + */ +bool p2p_check_random_mac(struct wlan_objmgr_psoc *soc, uint32_t vdev_id, + uint8_t *random_mac_addr); + +/** + * p2p_process_set_rand_mac() - process the set random mac command + * @set_filter_req: request data + * + * This function will process the set mac addr filter command. + * + * Return: QDF_STATUS_SUCCESS: if process successfully + * other: failed. + */ +QDF_STATUS p2p_process_set_rand_mac( + struct p2p_set_mac_filter_req *set_filter_req); + +/** + * p2p_process_set_rand_mac_rsp() - process the set random mac response + * @resp: response date + * + * This function will process the set mac addr filter event. + * + * Return: QDF_STATUS_SUCCESS: if process successfully + * other: failed. + */ +QDF_STATUS p2p_process_set_rand_mac_rsp(struct p2p_mac_filter_rsp *resp); + +/** + * p2p_del_all_rand_mac_vdev() - del all random mac filter in vdev + * @vdev: vdev object + * + * This function will del all random mac filter in vdev + * + * Return: void + */ +void p2p_del_all_rand_mac_vdev(struct wlan_objmgr_vdev *vdev); + +/** + * p2p_del_all_rand_mac_soc() - del all random mac filter in soc + * @soc: soc object + * + * This function will del all random mac filter in all vdev of soc + * + * Return: void + */ +void p2p_del_all_rand_mac_soc(struct wlan_objmgr_psoc *soc); + +/** + * p2p_rand_mac_tx() - handle random mac mgmt tx + * @tx_action: tx action context + * + * This function will check whether need to set random mac tx filter for a + * given mgmt tx request and do the mac addr filter process as needed. + * + * Return: void + */ +void p2p_rand_mac_tx(struct tx_action_context *tx_action); + +/** + * p2p_init_random_mac_vdev() - Init random mac data for vdev + * @p2p_vdev_obj: p2p vdev private object + * + * This function will init the per vdev random mac data structure. + * + * Return: void + */ +void p2p_init_random_mac_vdev(struct p2p_vdev_priv_obj *p2p_vdev_obj); + +/** + * p2p_deinit_random_mac_vdev() - Init random mac data for vdev + * @p2p_vdev_obj: p2p vdev private object + * + * This function will deinit the per vdev random mac data structure. + * + * Return: void + */ +void p2p_deinit_random_mac_vdev(struct p2p_vdev_priv_obj *p2p_vdev_obj); + +#endif /* _WLAN_P2P_OFF_CHAN_TX_H_ */ diff --git a/p2p/core/src/wlan_p2p_roc.c b/p2p/core/src/wlan_p2p_roc.c new file mode 100644 index 0000000000..10db8e74d4 --- /dev/null +++ b/p2p/core/src/wlan_p2p_roc.c @@ -0,0 +1,929 @@ +/* + * Copyright (c) 2017-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. + */ + +/** + * DOC: This file contains RoC API definitions + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "wlan_p2p_public_struct.h" +#include "wlan_p2p_tgt_api.h" +#include "wlan_p2p_ucfg_api.h" +#include "wlan_p2p_roc.h" +#include "wlan_p2p_main.h" +#include "wlan_p2p_off_chan_tx.h" + +/** + * p2p_mgmt_rx_ops() - register or unregister rx callback + * @psoc: psoc object + * @isregister: register if true, unregister if false + * + * This function registers or unregisters rx callback to mgmt txrx + * component. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_mgmt_rx_ops(struct wlan_objmgr_psoc *psoc, + bool isregister) +{ + struct mgmt_txrx_mgmt_frame_cb_info frm_cb_info; + QDF_STATUS status; + + p2p_debug("psoc:%pK, is register rx:%d", psoc, isregister); + + frm_cb_info.frm_type = MGMT_PROBE_REQ; + frm_cb_info.mgmt_rx_cb = tgt_p2p_mgmt_frame_rx_cb; + + if (isregister) + status = wlan_mgmt_txrx_register_rx_cb(psoc, + WLAN_UMAC_COMP_P2P, &frm_cb_info, 1); + else + status = wlan_mgmt_txrx_deregister_rx_cb(psoc, + WLAN_UMAC_COMP_P2P, &frm_cb_info, 1); + + return status; +} + +/** + * p2p_scan_start() - Start scan + * @roc_ctx: remain on channel request + * + * This function trigger a start scan request to scan component. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_scan_start(struct p2p_roc_context *roc_ctx) +{ + QDF_STATUS status; + struct scan_start_request *req; + struct wlan_objmgr_vdev *vdev; + struct p2p_soc_priv_obj *p2p_soc_obj = roc_ctx->p2p_soc_obj; + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc( + p2p_soc_obj->soc, roc_ctx->vdev_id, + WLAN_P2P_ID); + if (!vdev) { + p2p_err("vdev is NULL"); + return QDF_STATUS_E_INVAL; + } + + req = qdf_mem_malloc(sizeof(*req)); + if (!req) { + p2p_err("failed to alloc scan start request"); + status = QDF_STATUS_E_NOMEM; + goto fail; + } + + ucfg_scan_init_default_params(vdev, req); + roc_ctx->scan_id = ucfg_scan_get_scan_id(p2p_soc_obj->soc); + req->vdev = vdev; + req->scan_req.scan_id = roc_ctx->scan_id; + req->scan_req.p2p_scan_type = SCAN_P2P_LISTEN; + req->scan_req.scan_req_id = p2p_soc_obj->scan_req_id; + req->scan_req.chan_list.num_chan = 1; + req->scan_req.chan_list.chan[0].freq = wlan_chan_to_freq(roc_ctx->chan); + req->scan_req.dwell_time_passive = roc_ctx->duration; + req->scan_req.dwell_time_active = 0; + req->scan_req.scan_priority = SCAN_PRIORITY_HIGH; + req->scan_req.num_bssid = 1; + qdf_set_macaddr_broadcast(&req->scan_req.bssid_list[0]); + + status = ucfg_scan_start(req); + + p2p_debug("start scan, scan req id:%d, scan id:%d, status:%d", + p2p_soc_obj->scan_req_id, roc_ctx->scan_id, status); +fail: + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); + + return status; +} + +/** + * p2p_scan_abort() - Abort scan + * @roc_ctx: remain on channel request + * + * This function trigger an abort scan request to scan component. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_scan_abort(struct p2p_roc_context *roc_ctx) +{ + QDF_STATUS status; + struct scan_cancel_request *req; + struct wlan_objmgr_vdev *vdev; + struct p2p_soc_priv_obj *p2p_soc_obj = roc_ctx->p2p_soc_obj; + + p2p_debug("abort scan, scan req id:%d, scan id:%d", + p2p_soc_obj->scan_req_id, roc_ctx->scan_id); + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc( + p2p_soc_obj->soc, roc_ctx->vdev_id, + WLAN_P2P_ID); + if (!vdev) { + p2p_err("vdev is NULL"); + return QDF_STATUS_E_INVAL; + } + + req = qdf_mem_malloc(sizeof(*req)); + if (!req) { + p2p_err("failed to alloc scan cancel request"); + status = QDF_STATUS_E_NOMEM; + goto fail; + } + + req->vdev = vdev; + req->cancel_req.requester = p2p_soc_obj->scan_req_id; + req->cancel_req.scan_id = roc_ctx->scan_id; + req->cancel_req.vdev_id = roc_ctx->vdev_id; + req->cancel_req.req_type = WLAN_SCAN_CANCEL_SINGLE; + + qdf_mtrace(QDF_MODULE_ID_P2P, QDF_MODULE_ID_SCAN, + req->cancel_req.req_type, + req->vdev->vdev_objmgr.vdev_id, req->cancel_req.scan_id); + status = ucfg_scan_cancel(req); + + p2p_debug("abort scan, scan req id:%d, scan id:%d, status:%d", + p2p_soc_obj->scan_req_id, roc_ctx->scan_id, status); +fail: + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); + + return status; +} + +/** + * p2p_send_roc_event() - Send roc event + * @roc_ctx: remain on channel request + * @evt: roc event information + * + * This function send out roc event to up layer. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_send_roc_event( + struct p2p_roc_context *roc_ctx, enum p2p_roc_event evt) +{ + struct p2p_soc_priv_obj *p2p_soc_obj; + struct p2p_event p2p_evt; + struct p2p_start_param *start_param; + + p2p_soc_obj = roc_ctx->p2p_soc_obj; + if (!p2p_soc_obj || !(p2p_soc_obj->start_param)) { + p2p_err("Invalid p2p soc object or start parameters"); + return QDF_STATUS_E_INVAL; + } + start_param = p2p_soc_obj->start_param; + if (!(start_param->event_cb)) { + p2p_err("Invalid p2p event callback to up layer"); + return QDF_STATUS_E_INVAL; + } + + p2p_evt.vdev_id = roc_ctx->vdev_id; + p2p_evt.roc_event = evt; + p2p_evt.cookie = (uint64_t)roc_ctx->id; + p2p_evt.chan = roc_ctx->chan; + p2p_evt.duration = roc_ctx->duration; + + p2p_debug("p2p soc_obj:%pK, roc_ctx:%pK, vdev_id:%d, roc_event:" + "%d, cookie:%llx, chan:%d, duration:%d", p2p_soc_obj, + roc_ctx, p2p_evt.vdev_id, p2p_evt.roc_event, + p2p_evt.cookie, p2p_evt.chan, p2p_evt.duration); + + start_param->event_cb(start_param->event_cb_data, &p2p_evt); + + return QDF_STATUS_SUCCESS; +} + +/** + * p2p_destroy_roc_ctx() - destroy roc ctx + * @roc_ctx: remain on channel request + * @up_layer_event: if send uplayer event + * @in_roc_queue: if roc context in roc queue + * + * This function destroy roc context. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_destroy_roc_ctx(struct p2p_roc_context *roc_ctx, + bool up_layer_event, bool in_roc_queue) +{ + QDF_STATUS status = QDF_STATUS_SUCCESS; + struct p2p_soc_priv_obj *p2p_soc_obj = roc_ctx->p2p_soc_obj; + + p2p_debug("p2p_soc_obj:%pK, roc_ctx:%pK, up_layer_event:%d, in_roc_queue:%d", + p2p_soc_obj, roc_ctx, up_layer_event, in_roc_queue); + + if (up_layer_event) { + if (roc_ctx->roc_state < ROC_STATE_ON_CHAN) + p2p_send_roc_event(roc_ctx, ROC_EVENT_READY_ON_CHAN); + p2p_send_roc_event(roc_ctx, ROC_EVENT_COMPLETED); + } + + if (in_roc_queue) { + status = qdf_list_remove_node(&p2p_soc_obj->roc_q, + (qdf_list_node_t *)roc_ctx); + if (QDF_STATUS_SUCCESS != status) + p2p_err("Failed to remove roc req, status %d", status); + } + + qdf_idr_remove(&p2p_soc_obj->p2p_idr, roc_ctx->id); + qdf_mem_free(roc_ctx); + + return status; +} + +/** + * p2p_execute_cancel_roc_req() - Execute cancel roc request + * @roc_ctx: remain on channel request + * + * This function stop roc timer, abort scan and unregister mgmt rx + * callbak. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_execute_cancel_roc_req( + struct p2p_roc_context *roc_ctx) +{ + QDF_STATUS status; + struct p2p_soc_priv_obj *p2p_soc_obj = roc_ctx->p2p_soc_obj; + + p2p_debug("p2p soc obj:%pK, roc ctx:%pK, vdev_id:%d, scan_id:%d, tx ctx:%pK, chan:%d, phy_mode:%d, duration:%d, roc_type:%d, roc_state:%d", + p2p_soc_obj, roc_ctx, roc_ctx->vdev_id, + roc_ctx->scan_id, roc_ctx->tx_ctx, roc_ctx->chan, + roc_ctx->phy_mode, roc_ctx->duration, + roc_ctx->roc_type, roc_ctx->roc_state); + + roc_ctx->roc_state = ROC_STATE_CANCEL_IN_PROG; + qdf_event_reset(&p2p_soc_obj->cancel_roc_done); + status = qdf_mc_timer_stop(&roc_ctx->roc_timer); + if (status != QDF_STATUS_SUCCESS) + p2p_err("Failed to stop roc timer, roc %pK", roc_ctx); + + status = p2p_scan_abort(roc_ctx); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("Failed to abort scan, status:%d, destroy roc %pK", + status, roc_ctx); + qdf_mc_timer_destroy(&roc_ctx->roc_timer); + p2p_mgmt_rx_ops(p2p_soc_obj->soc, false); + p2p_destroy_roc_ctx(roc_ctx, true, true); + qdf_event_set(&p2p_soc_obj->cancel_roc_done); + return status; + } + + return QDF_STATUS_SUCCESS; +} + +/** + * p2p_roc_timeout() - Callback for roc timeout + * @pdata: pointer to p2p soc private object + * + * This function is callback for roc time out. + * + * Return: None + */ +static void p2p_roc_timeout(void *pdata) +{ + struct p2p_roc_context *roc_ctx; + struct p2p_soc_priv_obj *p2p_soc_obj; + + p2p_debug("p2p soc obj:%pK", pdata); + + p2p_soc_obj = pdata; + if (!p2p_soc_obj) { + p2p_err("Invalid p2p soc object"); + return; + } + + roc_ctx = p2p_find_current_roc_ctx(p2p_soc_obj); + if (!roc_ctx) { + p2p_debug("No P2P roc is pending"); + return; + } + + p2p_debug("p2p soc obj:%pK, roc ctx:%pK, vdev_id:%d, scan_id:%d, tx ctx:%pK, chan:%d, phy_mode:%d, duration:%d, roc_type:%d, roc_state:%d", + roc_ctx->p2p_soc_obj, roc_ctx, roc_ctx->vdev_id, + roc_ctx->scan_id, roc_ctx->tx_ctx, roc_ctx->chan, + roc_ctx->phy_mode, roc_ctx->duration, + roc_ctx->roc_type, roc_ctx->roc_state); + + if (roc_ctx->roc_state == ROC_STATE_CANCEL_IN_PROG) { + p2p_err("Cancellation already in progress"); + return; + } + p2p_execute_cancel_roc_req(roc_ctx); +} + +/** + * p2p_execute_roc_req() - Execute roc request + * @roc_ctx: remain on channel request + * + * This function init roc timer, start scan and register mgmt rx + * callbak. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_execute_roc_req(struct p2p_roc_context *roc_ctx) +{ + QDF_STATUS status; + uint32_t go_num; + struct p2p_soc_priv_obj *p2p_soc_obj = roc_ctx->p2p_soc_obj; + + p2p_debug("p2p soc obj:%pK, roc ctx:%pK, vdev_id:%d, scan_id:%d, tx ctx:%pK, chan:%d, phy_mode:%d, duration:%d, roc_type:%d, roc_state:%d", + p2p_soc_obj, roc_ctx, roc_ctx->vdev_id, + roc_ctx->scan_id, roc_ctx->tx_ctx, roc_ctx->chan, + roc_ctx->phy_mode, roc_ctx->duration, + roc_ctx->roc_type, roc_ctx->roc_state); + + /* prevent runtime suspend */ + qdf_runtime_pm_prevent_suspend(&p2p_soc_obj->roc_runtime_lock); + + status = qdf_mc_timer_init(&roc_ctx->roc_timer, + QDF_TIMER_TYPE_SW, p2p_roc_timeout, + p2p_soc_obj); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("failed to init roc timer, status:%d", status); + goto fail; + } + + roc_ctx->roc_state = ROC_STATE_REQUESTED; + if (roc_ctx->duration < P2P_MAX_ROC_DURATION) { + go_num = policy_mgr_mode_specific_connection_count( + p2p_soc_obj->soc, PM_P2P_GO_MODE, NULL); + p2p_debug("present go number:%d", go_num); + if (go_num) + roc_ctx->duration *= P2P_ROC_DURATION_MULTI_GO_PRESENT; + else + roc_ctx->duration *= P2P_ROC_DURATION_MULTI_GO_ABSENT; + /* this is to protect too huge value if some customers + * give a higher value from supplicant + */ + if (roc_ctx->duration > P2P_MAX_ROC_DURATION) + roc_ctx->duration = P2P_MAX_ROC_DURATION; + } + status = p2p_scan_start(roc_ctx); + if (status != QDF_STATUS_SUCCESS) { + qdf_mc_timer_destroy(&roc_ctx->roc_timer); + p2p_err("Failed to start scan, status:%d", status); + goto fail; + } + +fail: + if (status != QDF_STATUS_SUCCESS) { + p2p_destroy_roc_ctx(roc_ctx, true, true); + qdf_runtime_pm_allow_suspend( + &p2p_soc_obj->roc_runtime_lock); + return status; + } + + p2p_soc_obj->cur_roc_vdev_id = roc_ctx->vdev_id; + status = p2p_mgmt_rx_ops(p2p_soc_obj->soc, true); + if (status != QDF_STATUS_SUCCESS) + p2p_err("Failed to register mgmt rx callback, status:%d", + status); + + return status; +} + +/** + * p2p_find_roc_ctx() - Find out roc context by cookie + * @p2p_soc_obj: p2p psoc private object + * @cookie: cookie is the key to find out roc context + * + * This function find out roc context by cookie from p2p psoc private + * object + * + * Return: Pointer to roc context - success + * NULL - failure + */ +static struct p2p_roc_context *p2p_find_roc_ctx( + struct p2p_soc_priv_obj *p2p_soc_obj, uint64_t cookie) +{ + struct p2p_roc_context *curr_roc_ctx; + qdf_list_node_t *p_node; + QDF_STATUS status; + + p2p_debug("p2p soc obj:%pK, cookie:%llx", p2p_soc_obj, cookie); + + status = qdf_list_peek_front(&p2p_soc_obj->roc_q, &p_node); + while (QDF_IS_STATUS_SUCCESS(status)) { + curr_roc_ctx = qdf_container_of(p_node, + struct p2p_roc_context, node); + if ((uintptr_t) curr_roc_ctx == cookie) + return curr_roc_ctx; + status = qdf_list_peek_next(&p2p_soc_obj->roc_q, + p_node, &p_node); + } + + return NULL; +} + +/** + * p2p_process_scan_start_evt() - Process scan start event + * @roc_ctx: remain on channel request + * + * This function process scan start event. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_process_scan_start_evt( + struct p2p_roc_context *roc_ctx) +{ + roc_ctx->roc_state = ROC_STATE_STARTED; + p2p_debug("scan started, roc ctx:%pK, scan id:%d", + roc_ctx, roc_ctx->scan_id); + + return QDF_STATUS_SUCCESS; +} + +/** + * p2p_process_ready_on_channel_evt() - Process ready on channel event + * @roc_ctx: remain on channel request + * + * This function process ready on channel event. Starts roc timer. + * Indicates this event to up layer if this is user request roc. Sends + * mgmt frame if this is off channel rx roc. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_process_ready_on_channel_evt( + struct p2p_roc_context *roc_ctx) +{ + uint64_t cookie; + struct p2p_soc_priv_obj *p2p_soc_obj; + QDF_STATUS status; + + p2p_soc_obj = roc_ctx->p2p_soc_obj; + roc_ctx->roc_state = ROC_STATE_ON_CHAN; + + p2p_debug("p2p soc obj:%pK, roc ctx:%pK, vdev_id:%d, scan_id:%d, tx ctx:%pK, chan:%d, phy_mode:%d, duration:%d, roc_type:%d, roc_state:%d", + p2p_soc_obj, roc_ctx, roc_ctx->vdev_id, + roc_ctx->scan_id, roc_ctx->tx_ctx, roc_ctx->chan, + roc_ctx->phy_mode, roc_ctx->duration, + roc_ctx->roc_type, roc_ctx->roc_state); + + status = qdf_mc_timer_start(&roc_ctx->roc_timer, + (roc_ctx->duration + P2P_EVENT_PROPAGATE_TIME)); + if (status != QDF_STATUS_SUCCESS) + p2p_err("Remain on Channel timer start failed"); + if (roc_ctx->roc_type == USER_REQUESTED) { + p2p_debug("user required roc, send roc event"); + status = p2p_send_roc_event(roc_ctx, + ROC_EVENT_READY_ON_CHAN); + } + + cookie = (uintptr_t)roc_ctx; + /* ready to tx frame */ + p2p_ready_to_tx_frame(p2p_soc_obj, cookie); + + return status; +} + +/** + * p2p_process_scan_complete_evt() - Process scan complete event + * @roc_ctx: remain on channel request + * + * This function process scan complete event. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_process_scan_complete_evt( + struct p2p_roc_context *roc_ctx) +{ + QDF_STATUS status; + qdf_list_node_t *next_node; + uint32_t size; + struct p2p_soc_priv_obj *p2p_soc_obj = roc_ctx->p2p_soc_obj; + + p2p_debug("p2p soc obj:%pK, roc ctx:%pK, vdev_id:%d, scan_id:%d, tx ctx:%pK, chan:%d, phy_mode:%d, duration:%d, roc_type:%d, roc_state:%d", + p2p_soc_obj, roc_ctx, roc_ctx->vdev_id, + roc_ctx->scan_id, roc_ctx->tx_ctx, roc_ctx->chan, + roc_ctx->phy_mode, roc_ctx->duration, + roc_ctx->roc_type, roc_ctx->roc_state); + + /* allow runtime suspend */ + qdf_runtime_pm_allow_suspend(&p2p_soc_obj->roc_runtime_lock); + + if (QDF_TIMER_STATE_RUNNING == + qdf_mc_timer_get_current_state(&roc_ctx->roc_timer)) { + status = qdf_mc_timer_stop(&roc_ctx->roc_timer); + if (status != QDF_STATUS_SUCCESS) + p2p_err("Failed to stop roc timer"); + } + + status = qdf_mc_timer_destroy(&roc_ctx->roc_timer); + if (status != QDF_STATUS_SUCCESS) + p2p_err("Failed to destroy roc timer"); + + status = p2p_mgmt_rx_ops(p2p_soc_obj->soc, false); + p2p_soc_obj->cur_roc_vdev_id = P2P_INVALID_VDEV_ID; + if (status != QDF_STATUS_SUCCESS) + p2p_err("Failed to deregister mgmt rx callback"); + + if (roc_ctx->roc_type == USER_REQUESTED) + status = p2p_send_roc_event(roc_ctx, + ROC_EVENT_COMPLETED); + + p2p_destroy_roc_ctx(roc_ctx, false, true); + qdf_event_set(&p2p_soc_obj->cancel_roc_done); + + size = qdf_list_size(&p2p_soc_obj->roc_q); + p2p_debug("P2P roc queue size is %d", status); + if (size > 0) { + status = qdf_list_peek_front(&p2p_soc_obj->roc_q, + &next_node); + if (QDF_STATUS_SUCCESS != status) { + p2p_err("Failed to peek roc req from front, status %d", + status); + return status; + } + roc_ctx = qdf_container_of(next_node, + struct p2p_roc_context, node); + status = p2p_execute_roc_req(roc_ctx); + } + return status; +} + +QDF_STATUS p2p_mgmt_rx_action_ops(struct wlan_objmgr_psoc *psoc, + bool isregister) +{ + struct mgmt_txrx_mgmt_frame_cb_info frm_cb_info[2]; + QDF_STATUS status; + + p2p_debug("psoc:%pK, is register rx:%d", psoc, isregister); + + frm_cb_info[0].frm_type = MGMT_ACTION_VENDOR_SPECIFIC; + frm_cb_info[0].mgmt_rx_cb = tgt_p2p_mgmt_frame_rx_cb; + frm_cb_info[1].frm_type = MGMT_ACTION_CATEGORY_VENDOR_SPECIFIC; + frm_cb_info[1].mgmt_rx_cb = tgt_p2p_mgmt_frame_rx_cb; + + if (isregister) + status = wlan_mgmt_txrx_register_rx_cb(psoc, + WLAN_UMAC_COMP_P2P, frm_cb_info, 2); + else + status = wlan_mgmt_txrx_deregister_rx_cb(psoc, + WLAN_UMAC_COMP_P2P, frm_cb_info, 2); + + return status; +} + +struct p2p_roc_context *p2p_find_current_roc_ctx( + struct p2p_soc_priv_obj *p2p_soc_obj) +{ + struct p2p_roc_context *roc_ctx; + qdf_list_node_t *p_node; + QDF_STATUS status; + + status = qdf_list_peek_front(&p2p_soc_obj->roc_q, &p_node); + while (QDF_IS_STATUS_SUCCESS(status)) { + roc_ctx = qdf_container_of(p_node, + struct p2p_roc_context, node); + if (roc_ctx->roc_state != ROC_STATE_IDLE) { + p2p_debug("p2p soc obj:%pK, roc ctx:%pK, vdev_id" + ":%d, scan_id:%d, tx ctx:%pK, chan:" + "%d, phy_mode:%d, duration:%d, " + "roc_type:%d, roc_state:%d", + roc_ctx->p2p_soc_obj, roc_ctx, + roc_ctx->vdev_id, roc_ctx->scan_id, + roc_ctx->tx_ctx, roc_ctx->chan, + roc_ctx->phy_mode, roc_ctx->duration, + roc_ctx->roc_type, roc_ctx->roc_state); + + return roc_ctx; + } + status = qdf_list_peek_next(&p2p_soc_obj->roc_q, + p_node, &p_node); + } + + return NULL; +} + +struct p2p_roc_context *p2p_find_roc_by_tx_ctx( + struct p2p_soc_priv_obj *p2p_soc_obj, uint64_t cookie) +{ + struct p2p_roc_context *curr_roc_ctx; + qdf_list_node_t *p_node; + QDF_STATUS status; + + p2p_debug("p2p soc obj:%pK, cookie:%llx", p2p_soc_obj, cookie); + + status = qdf_list_peek_front(&p2p_soc_obj->roc_q, &p_node); + while (QDF_IS_STATUS_SUCCESS(status)) { + curr_roc_ctx = qdf_container_of(p_node, + struct p2p_roc_context, node); + if ((uintptr_t) curr_roc_ctx->tx_ctx == cookie) + return curr_roc_ctx; + status = qdf_list_peek_next(&p2p_soc_obj->roc_q, + p_node, &p_node); + } + + return NULL; +} + +struct p2p_roc_context *p2p_find_roc_by_chan( + struct p2p_soc_priv_obj *p2p_soc_obj, uint8_t chan) +{ + struct p2p_roc_context *roc_ctx; + qdf_list_node_t *p_node; + QDF_STATUS status; + + status = qdf_list_peek_front(&p2p_soc_obj->roc_q, &p_node); + while (QDF_IS_STATUS_SUCCESS(status)) { + roc_ctx = qdf_container_of(p_node, + struct p2p_roc_context, + node); + if (roc_ctx->chan == chan) { + p2p_debug("p2p soc obj:%pK, roc ctx:%pK, vdev_id:%d, scan_id:%d, tx ctx:%pK, chan:%d, phy_mode:%d, duration:%d, roc_type:%d, roc_state:%d", + roc_ctx->p2p_soc_obj, roc_ctx, + roc_ctx->vdev_id, roc_ctx->scan_id, + roc_ctx->tx_ctx, roc_ctx->chan, + roc_ctx->phy_mode, roc_ctx->duration, + roc_ctx->roc_type, roc_ctx->roc_state); + + return roc_ctx; + } + status = qdf_list_peek_next(&p2p_soc_obj->roc_q, + p_node, &p_node); + } + + return NULL; +} + +QDF_STATUS p2p_restart_roc_timer(struct p2p_roc_context *roc_ctx) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + if (QDF_TIMER_STATE_RUNNING == + qdf_mc_timer_get_current_state(&roc_ctx->roc_timer)) { + p2p_debug("roc timer is running"); + status = qdf_mc_timer_stop(&roc_ctx->roc_timer); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("Failed to stop roc timer"); + return status; + } + + status = qdf_mc_timer_start(&roc_ctx->roc_timer, + roc_ctx->duration); + if (status != QDF_STATUS_SUCCESS) + p2p_err("Remain on Channel timer start failed"); + } + + return status; +} + +QDF_STATUS p2p_cleanup_roc_sync( + struct p2p_soc_priv_obj *p2p_soc_obj, + struct wlan_objmgr_vdev *vdev) +{ + struct scheduler_msg msg = {0}; + struct p2p_cleanup_param *param; + QDF_STATUS status; + uint32_t vdev_id; + + if (!p2p_soc_obj) { + p2p_err("p2p soc context is NULL"); + return QDF_STATUS_E_FAILURE; + } + + p2p_debug("p2p_soc_obj:%pK, vdev:%pK", p2p_soc_obj, vdev); + param = qdf_mem_malloc(sizeof(*param)); + if (!param) { + p2p_err("failed to allocate cleanup param"); + return QDF_STATUS_E_NOMEM; + } + + param->p2p_soc_obj = p2p_soc_obj; + if (vdev) + vdev_id = (uint32_t)wlan_vdev_get_id(vdev); + else + vdev_id = P2P_INVALID_VDEV_ID; + param->vdev_id = vdev_id; + qdf_event_reset(&p2p_soc_obj->cleanup_roc_done); + msg.type = P2P_CLEANUP_ROC; + msg.bodyptr = param; + msg.callback = p2p_process_cmd; + status = scheduler_post_message(QDF_MODULE_ID_P2P, + QDF_MODULE_ID_P2P, + QDF_MODULE_ID_OS_IF, &msg); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("failed to post message"); + qdf_mem_free(param); + return status; + } + + status = qdf_wait_single_event( + &p2p_soc_obj->cleanup_roc_done, + P2P_WAIT_CLEANUP_ROC); + + if (status != QDF_STATUS_SUCCESS) + p2p_err("wait for cleanup roc timeout, %d", status); + + return status; +} + +QDF_STATUS p2p_process_cleanup_roc_queue( + struct p2p_cleanup_param *param) +{ + uint32_t vdev_id; + QDF_STATUS status, ret; + struct p2p_roc_context *roc_ctx; + qdf_list_node_t *p_node; + struct p2p_soc_priv_obj *p2p_soc_obj; + + if (!param || !(param->p2p_soc_obj)) { + p2p_err("Invalid cleanup param"); + return QDF_STATUS_E_FAILURE; + } + + p2p_soc_obj = param->p2p_soc_obj; + vdev_id = param->vdev_id; + + p2p_debug("clean up idle roc request, roc queue size:%d, vdev id:%d", + qdf_list_size(&p2p_soc_obj->roc_q), vdev_id); + status = qdf_list_peek_front(&p2p_soc_obj->roc_q, &p_node); + while (QDF_IS_STATUS_SUCCESS(status)) { + roc_ctx = qdf_container_of(p_node, + struct p2p_roc_context, node); + + p2p_debug("p2p soc obj:%pK, roc ctx:%pK, vdev_id:%d, scan_id:%d, tx ctx:%pK, chan:%d, phy_mode:%d, duration:%d, roc_type:%d, roc_state:%d", + roc_ctx->p2p_soc_obj, roc_ctx, + roc_ctx->vdev_id, roc_ctx->scan_id, + roc_ctx->tx_ctx, roc_ctx->chan, + roc_ctx->phy_mode, roc_ctx->duration, + roc_ctx->roc_type, roc_ctx->roc_state); + status = qdf_list_peek_next(&p2p_soc_obj->roc_q, + p_node, &p_node); + if ((roc_ctx->roc_state == ROC_STATE_IDLE) && + ((vdev_id == P2P_INVALID_VDEV_ID) || + (vdev_id == roc_ctx->vdev_id))) { + ret = qdf_list_remove_node( + &p2p_soc_obj->roc_q, + (qdf_list_node_t *)roc_ctx); + if (ret == QDF_STATUS_SUCCESS) + qdf_mem_free(roc_ctx); + else + p2p_err("Failed to remove roc ctx from queue"); + } + } + + p2p_debug("clean up started roc request, roc queue size:%d", + qdf_list_size(&p2p_soc_obj->roc_q)); + status = qdf_list_peek_front(&p2p_soc_obj->roc_q, &p_node); + while (QDF_IS_STATUS_SUCCESS(status)) { + roc_ctx = qdf_container_of(p_node, + struct p2p_roc_context, node); + + p2p_debug("p2p soc obj:%pK, roc ctx:%pK, vdev_id:%d, scan_id:%d, tx ctx:%pK, chan:%d, phy_mode:%d, duration:%d, roc_type:%d, roc_state:%d", + roc_ctx->p2p_soc_obj, roc_ctx, roc_ctx->vdev_id, + roc_ctx->scan_id, roc_ctx->tx_ctx, roc_ctx->chan, + roc_ctx->phy_mode, roc_ctx->duration, + roc_ctx->roc_type, roc_ctx->roc_state); + + status = qdf_list_peek_next(&p2p_soc_obj->roc_q, + p_node, &p_node); + if ((roc_ctx->roc_state != ROC_STATE_IDLE) && + ((vdev_id == P2P_INVALID_VDEV_ID) || + (vdev_id == roc_ctx->vdev_id))) { + if (roc_ctx->roc_state != + ROC_STATE_CANCEL_IN_PROG) + p2p_execute_cancel_roc_req(roc_ctx); + + ret = qdf_wait_single_event( + &p2p_soc_obj->cancel_roc_done, + P2P_WAIT_CANCEL_ROC); + p2p_debug("RoC cancellation done, return:%d", ret); + } + } + + qdf_event_set(&p2p_soc_obj->cleanup_roc_done); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS p2p_process_roc_req(struct p2p_roc_context *roc_ctx) +{ + struct p2p_soc_priv_obj *p2p_soc_obj; + struct p2p_roc_context *curr_roc_ctx; + QDF_STATUS status; + uint32_t size; + + p2p_soc_obj = roc_ctx->p2p_soc_obj; + + p2p_debug("p2p soc obj:%pK, roc ctx:%pK, vdev_id:%d, scan_id:%d, tx_ctx:%pK, chan:%d, phy_mode:%d, duration:%d, roc_type:%d, roc_state:%d", + p2p_soc_obj, roc_ctx, roc_ctx->vdev_id, + roc_ctx->scan_id, roc_ctx->tx_ctx, roc_ctx->chan, + roc_ctx->phy_mode, roc_ctx->duration, + roc_ctx->roc_type, roc_ctx->roc_state); + + status = qdf_list_insert_back(&p2p_soc_obj->roc_q, + &roc_ctx->node); + if (QDF_STATUS_SUCCESS != status) { + p2p_destroy_roc_ctx(roc_ctx, true, false); + p2p_debug("Failed to insert roc req, status %d", status); + return status; + } + + size = qdf_list_size(&p2p_soc_obj->roc_q); + if (size == 1) { + status = p2p_execute_roc_req(roc_ctx); + } else if (size > 1) { + curr_roc_ctx = p2p_find_current_roc_ctx(p2p_soc_obj); + /*TODO, to handle extend roc */ + } + + return status; +} + +QDF_STATUS p2p_process_cancel_roc_req( + struct cancel_roc_context *cancel_roc_ctx) +{ + struct p2p_soc_priv_obj *p2p_soc_obj; + struct p2p_roc_context *curr_roc_ctx; + QDF_STATUS status; + + p2p_soc_obj = cancel_roc_ctx->p2p_soc_obj; + curr_roc_ctx = p2p_find_roc_ctx(p2p_soc_obj, + cancel_roc_ctx->cookie); + + p2p_debug("p2p soc obj:%pK, cookie:%llx, roc ctx:%pK", + p2p_soc_obj, cancel_roc_ctx->cookie, curr_roc_ctx); + + if (!curr_roc_ctx) { + p2p_debug("Failed to find roc req by cookie, cookie %llx", + cancel_roc_ctx->cookie); + return QDF_STATUS_E_INVAL; + } + + if (curr_roc_ctx->roc_state == ROC_STATE_IDLE) { + status = p2p_destroy_roc_ctx(curr_roc_ctx, true, true); + } else if (curr_roc_ctx->roc_state == + ROC_STATE_CANCEL_IN_PROG) { + p2p_debug("Receive cancel roc req when roc req is canceling, cookie %llx", + cancel_roc_ctx->cookie); + status = QDF_STATUS_SUCCESS; + } else { + status = p2p_execute_cancel_roc_req(curr_roc_ctx); + } + + return status; +} + +void p2p_scan_event_cb(struct wlan_objmgr_vdev *vdev, + struct scan_event *event, void *arg) +{ + struct p2p_soc_priv_obj *p2p_soc_obj; + struct p2p_roc_context *curr_roc_ctx; + + p2p_debug("soc:%pK, scan event:%d", arg, event->type); + + p2p_soc_obj = (struct p2p_soc_priv_obj *)arg; + if (!p2p_soc_obj) { + p2p_err("Invalid P2P context"); + return; + } + + curr_roc_ctx = p2p_find_current_roc_ctx(p2p_soc_obj); + if (!curr_roc_ctx) { + p2p_err("Failed to find valid P2P roc context"); + return; + } + + qdf_mtrace(QDF_MODULE_ID_SCAN, QDF_MODULE_ID_P2P, event->type, + event->vdev_id, event->scan_id); + switch (event->type) { + case SCAN_EVENT_TYPE_STARTED: + p2p_process_scan_start_evt(curr_roc_ctx); + break; + case SCAN_EVENT_TYPE_FOREIGN_CHANNEL: + p2p_process_ready_on_channel_evt(curr_roc_ctx); + break; + case SCAN_EVENT_TYPE_COMPLETED: + case SCAN_EVENT_TYPE_DEQUEUED: + case SCAN_EVENT_TYPE_START_FAILED: + p2p_process_scan_complete_evt(curr_roc_ctx); + break; + default: + p2p_debug("drop scan event, %d", event->type); + } +} diff --git a/p2p/core/src/wlan_p2p_roc.h b/p2p/core/src/wlan_p2p_roc.h new file mode 100644 index 0000000000..5e49b14976 --- /dev/null +++ b/p2p/core/src/wlan_p2p_roc.h @@ -0,0 +1,247 @@ +/* + * Copyright (c) 2017-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. + */ + +/** + * DOC: Defines RoC API & structures + */ + +#ifndef _WLAN_P2P_ROC_H_ +#define _WLAN_P2P_ROC_H_ + +#include +#include +#include + +#define P2P_EVENT_PROPAGATE_TIME 10 +#define P2P_WAIT_CANCEL_ROC 1000 +#define P2P_WAIT_CLEANUP_ROC 2000 +#define P2P_MAX_ROC_DURATION 1500 + +#define P2P_ROC_DURATION_MULTI_GO_PRESENT 6 +#define P2P_ROC_DURATION_MULTI_GO_ABSENT 10 +#define P2P_ACTION_FRAME_DEFAULT_WAIT 200 + +struct wlan_objmgr_vdev; +struct scan_event; + +/** + * enum roc_type - user requested or off channel tx + * @USER_REQUESTED: Requested by supplicant + * @OFF_CHANNEL_TX: Issued internally for off channel tx + */ +enum roc_type { + USER_REQUESTED, + OFF_CHANNEL_TX, +}; + +/** + * enum roc_state - P2P RoC state + * @ROC_STATE_IDLE: RoC not yet started or completed + * @ROC_STATE_REQUESTED: Sent scan command to scan manager + * @ROC_STATE_STARTED: Got started event from scan manager + * @ROC_STATE_ON_CHAN: Got foreign channel event from SCM + * @ROC_STATE_CANCEL_IN_PROG: Requested abort scan to SCM + * @ROC_STATE_INVALID: We should not come to this state + */ +enum roc_state { + ROC_STATE_IDLE = 0, + ROC_STATE_REQUESTED, + ROC_STATE_STARTED, + ROC_STATE_ON_CHAN, + ROC_STATE_CANCEL_IN_PROG, + ROC_STATE_INVALID, +}; + +/** + * struct p2p_roc_context - RoC request context + * @node: Node for next element in the list + * @p2p_soc_obj: Pointer to SoC global p2p private object + * @vdev_id: Vdev id on which this request has come + * @scan_id: Scan id given by scan component for this roc req + * @tx_ctx: TX context if this ROC is for tx MGMT + * @chan: Chan for which this RoC has been requested + * @phy_mode: PHY mode + * @duration: Duration for the RoC + * @roc_type: RoC type User requested or internal + * @roc_timer: RoC timer + * @roc_state: Roc state + * @id: identifier of roc + */ +struct p2p_roc_context { + qdf_list_node_t node; + struct p2p_soc_priv_obj *p2p_soc_obj; + uint32_t vdev_id; + uint32_t scan_id; + void *tx_ctx; + uint8_t chan; + uint8_t phy_mode; + uint32_t duration; + enum roc_type roc_type; + qdf_mc_timer_t roc_timer; + enum roc_state roc_state; + int32_t id; +}; + +/** + * struct cancel_roc_context - p2p cancel roc context + * @p2p_soc_obj: Pointer to SoC global p2p private object + * @cookie: Cookie which is given by supplicant + */ +struct cancel_roc_context { + struct p2p_soc_priv_obj *p2p_soc_obj; + uint64_t cookie; +}; + +/** + * struct p2p_cleanup_param - p2p cleanup parameters + * @p2p_soc_obj: Pointer to SoC global p2p private object + * @vdev_id: vdev id + */ +struct p2p_cleanup_param { + struct p2p_soc_priv_obj *p2p_soc_obj; + uint32_t vdev_id; +}; + +/** + * p2p_mgmt_rx_action_ops() - register or unregister rx action callback + * @psoc: psoc object + * @isregister: register if true, unregister if false + * + * This function registers or unregisters rx action frame callback to + * mgmt txrx component. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_mgmt_rx_action_ops(struct wlan_objmgr_psoc *psoc, + bool isregister); + +/** + * p2p_find_current_roc_ctx() - Find out roc context in progressing + * @p2p_soc_obj: p2p psoc private object + * + * This function finds out roc context in progressing from p2p psoc + * private object + * + * Return: Pointer to roc context - success + * NULL - failure + */ +struct p2p_roc_context *p2p_find_current_roc_ctx( + struct p2p_soc_priv_obj *p2p_soc_obj); + +/** + * p2p_find_roc_by_tx_ctx() - Find out roc context by tx context + * @p2p_soc_obj: p2p psoc private object + * @cookie: cookie is the key to find out roc context + * + * This function finds out roc context by tx context from p2p psoc + * private object + * + * Return: Pointer to roc context - success + * NULL - failure + */ +struct p2p_roc_context *p2p_find_roc_by_tx_ctx( + struct p2p_soc_priv_obj *p2p_soc_obj, uint64_t cookie); + +/** + * p2p_find_roc_by_chan() - Find out roc context by channel + * @p2p_soc_obj: p2p psoc private object + * @chan: channel of the ROC + * + * This function finds out roc context by channel from p2p psoc + * private object + * + * Return: Pointer to roc context - success + * NULL - failure + */ +struct p2p_roc_context *p2p_find_roc_by_chan( + struct p2p_soc_priv_obj *p2p_soc_obj, uint8_t chan); + +/** + * p2p_restart_roc_timer() - Restarts roc timer + * @roc_ctx: remain on channel context + * + * This function restarts roc timer with updated duration. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_restart_roc_timer(struct p2p_roc_context *roc_ctx); + +/** + * p2p_cleanup_roc_sync() - Cleanup roc context in queue + * @p2p_soc_obj: p2p psoc private object + * @vdev: vdev object + * + * This function cleanup roc context in queue, include the roc + * context in progressing until cancellation done. To avoid deadlock, + * don't call from scheduler thread. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_cleanup_roc_sync( + struct p2p_soc_priv_obj *p2p_soc_obj, + struct wlan_objmgr_vdev *vdev); + +/** + * p2p_process_cleanup_roc_queue() - process the message to cleanup roc + * @param: pointer to cleanup parameters + * + * This function process the message to cleanup roc context in queue, + * include the roc context in progressing. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_process_cleanup_roc_queue( + struct p2p_cleanup_param *param); + +/** + * p2p_process_roc_req() - Process roc request + * @roc_ctx: roc request context + * + * This function handles roc request. It will call API from scan/mgmt + * txrx component. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_process_roc_req(struct p2p_roc_context *roc_ctx); + +/** + * p2p_process_cancel_roc_req() - Process cancel roc request + * @cancel_roc_ctx: cancel roc request context + * + * This function cancel roc request by cookie. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_process_cancel_roc_req( + struct cancel_roc_context *cancel_roc_ctx); + +/** + * p2p_scan_event_cb() - Process scan event + * @vdev: vdev associated to this scan event + * @event: event information + * @arg: registered arguments + * + * This function handles P2P scan event and deliver P2P event to HDD + * layer by registered callback. + * + * Return: None + */ +void p2p_scan_event_cb(struct wlan_objmgr_vdev *vdev, + struct scan_event *event, void *arg); + +#endif /* _WLAN_P2P_ROC_H_ */ diff --git a/p2p/dispatcher/inc/wlan_p2p_cfg.h b/p2p/dispatcher/inc/wlan_p2p_cfg.h new file mode 100644 index 0000000000..3ee037f5a0 --- /dev/null +++ b/p2p/dispatcher/inc/wlan_p2p_cfg.h @@ -0,0 +1,128 @@ +/* + * 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. + */ + +#if !defined(CONFIG_P2P_H__) +#define CONFIG_P2P_H__ + +#include "cfg_define.h" +#include "cfg_converged.h" +#include "qdf_types.h" + +/* + * + * gGoKeepAlivePeriod - P2P GO keep alive period. + * @Min: 1 + * @Max: 65535 + * @Default: 20 + * + * This is P2P GO keep alive period. + * + * Related: None. + * + * Supported Feature: P2P + * + * Usage: Internal/External + * + * + */ +#define CFG_GO_KEEP_ALIVE_PERIOD CFG_INI_UINT( \ + "gGoKeepAlivePeriod", \ + 1, \ + 65535, \ + 20, \ + CFG_VALUE_OR_DEFAULT, \ + "P2P GO keep alive period") + +/* + * + * gGoLinkMonitorPeriod - period where link is idle and where + * we send NULL frame + * @Min: 3 + * @Max: 50 + * @Default: 10 + * + * This is period where link is idle and where we send NULL frame for P2P GO. + * + * Related: None. + * + * Supported Feature: P2P + * + * Usage: Internal/External + * + * + */ +#define CFG_GO_LINK_MONITOR_PERIOD CFG_INI_UINT( \ + "gGoLinkMonitorPeriod", \ + 3, \ + 50, \ + 10, \ + CFG_VALUE_OR_DEFAULT, \ + "period where link is idle and where we send NULL frame") + +/* + * + * isP2pDeviceAddrAdministrated - Enables to derive the P2P MAC address from + * the primary MAC address + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This ini is used to enable/disable to derive the P2P MAC address from the + * primary MAC address. + * + * Related: None. + * + * Supported Feature: P2P + * + * Usage: Internal/External + * + * + */ +#define CFG_P2P_DEVICE_ADDRESS_ADMINISTRATED CFG_INI_BOOL( \ + "isP2pDeviceAddrAdministrated", \ + 1, \ + "derive the P2P MAC address from the primary MAC address") + +/* + * + * gSkipDfsChannelInP2pSearch - Skip DFS Channel in case of P2P Search + * @Min: 0 + * @Max: 1 + * @Default: 1 + * + * This ini is used to disable(skip) dfs channel in p2p search. + * Related: None. + * + * Supported Feature: DFS P2P + * + * Usage: Internal/External + * + * + */ +#define CFG_ENABLE_SKIP_DFS_IN_P2P_SEARCH CFG_INI_BOOL( \ + "gSkipDfsChannelInP2pSearch", \ + 1, \ + "skip dfs channel in p2p search") + +#define CFG_P2P_ALL \ + CFG(CFG_GO_KEEP_ALIVE_PERIOD) \ + CFG(CFG_GO_LINK_MONITOR_PERIOD) \ + CFG(CFG_P2P_DEVICE_ADDRESS_ADMINISTRATED) \ + CFG(CFG_ENABLE_SKIP_DFS_IN_P2P_SEARCH) + +#endif diff --git a/p2p/dispatcher/inc/wlan_p2p_cfg_api.h b/p2p/dispatcher/inc/wlan_p2p_cfg_api.h new file mode 100644 index 0000000000..18e86055bc --- /dev/null +++ b/p2p/dispatcher/inc/wlan_p2p_cfg_api.h @@ -0,0 +1,76 @@ +/* + * 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. + */ + +/** + * DOC: Contains p2p configures interface definitions + */ + +#ifndef _WLAN_P2P_CFG_API_H_ +#define _WLAN_P2P_CFG_API_H_ + +#include + +struct wlan_objmgr_psoc; + +/** + * cfg_p2p_get_go_keepalive_period() - get go keepalive period + * @psoc: pointer to psoc object + * @period: go keepalive period + * + * This function gets go keepalive period to p2p component + */ +QDF_STATUS +cfg_p2p_get_go_keepalive_period(struct wlan_objmgr_psoc *psoc, + uint32_t *period); + +/** + * cfg_p2p_get_go_link_monitor_period() - get go link monitor period + * @psoc: pointer to psoc object + * @period: go link monitor period + * + * This function gets go link monitor period to p2p component + */ +QDF_STATUS +cfg_p2p_get_go_link_monitor_period(struct wlan_objmgr_psoc *psoc, + uint32_t *period); + +/** + * cfg_p2p_get_device_addr_admin() - get enable/disable p2p device + * addr administrated + * @psoc: pointer to psoc object + * @enable: enable/disable p2p device addr administrated + * + * This function gets enable/disable p2p device addr administrated + */ +QDF_STATUS +cfg_p2p_get_device_addr_admin(struct wlan_objmgr_psoc *psoc, + bool *enable); + +/** + * cfg_p2p_get_skip_dfs_channel_p2p_search() - get skip dfs channel + * in p2p search + * @psoc: pointer to psoc object + * @enable: enable/disable skip dfs channel in p2p search + * + * This function gets enable/disable skip dfs channel in p2p search + */ +QDF_STATUS +cfg_p2p_get_skip_dfs_channel_p2p_search(struct wlan_objmgr_psoc *psoc, + bool *enable); + +#endif /* _WLAN_P2P_CFG_API_H_ */ diff --git a/p2p/dispatcher/inc/wlan_p2p_public_struct.h b/p2p/dispatcher/inc/wlan_p2p_public_struct.h new file mode 100644 index 0000000000..d1c597ff35 --- /dev/null +++ b/p2p/dispatcher/inc/wlan_p2p_public_struct.h @@ -0,0 +1,272 @@ +/* + * Copyright (c) 2017-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. + */ + +/** + * DOC: Contains p2p public data structure definations + */ + +#ifndef _WLAN_P2P_PUBLIC_STRUCT_H_ +#define _WLAN_P2P_PUBLIC_STRUCT_H_ + +#include + +#define P2P_MAX_NOA_DESC 4 + +/** + * struct p2p_ps_params - P2P powersave related params + * @opp_ps: opportunistic power save + * @ctwindow: CT window + * @count: count + * @duration: duration + * @interval: interval + * @single_noa_duration: single shot noa duration + * @ps_selection: power save selection + * @session_id: session id + */ +struct p2p_ps_params { + uint8_t opp_ps; + uint32_t ctwindow; + uint8_t count; + uint32_t duration; + uint32_t interval; + uint32_t single_noa_duration; + uint8_t ps_selection; + uint8_t session_id; +}; + +/** + * struct p2p_roc_req - P2P roc request + * @vdev_id: Vdev id on which this request has come + * @chan: Chan for which this RoC has been requested + * @phy_mode: PHY mode + * @duration: Duration for the RoC + */ +struct p2p_roc_req { + uint32_t vdev_id; + uint32_t chan; + uint32_t phy_mode; + uint32_t duration; +}; + +/** + * enum p2p_roc_event - P2P RoC event + * @ROC_EVENT_READY_ON_CHAN: RoC has started now + * @ROC_EVENT_COMPLETED: RoC has been completed + * @ROC_EVENT_INAVLID: Invalid event + */ +enum p2p_roc_event { + ROC_EVENT_READY_ON_CHAN = 0, + ROC_EVENT_COMPLETED, + ROC_EVENT_INAVLID, +}; + +/** + * struct p2p_event - p2p event + * @vdev_id: Vdev id + * @roc_event: RoC event + * @cookie: Cookie which is given to supplicant for this roc req + * @chan: Chan for which this RoC has been requested + * @duration: Duration for the RoC + */ +struct p2p_event { + uint32_t vdev_id; + enum p2p_roc_event roc_event; + uint64_t cookie; + uint32_t chan; + uint32_t duration; +}; + +/** + * struct p2p_rx_mgmt_frame - rx mgmt frame structure + * @frame_len: Frame length + * @rx_chan: RX channel + * @vdev_id: Vdev id + * @frm_type: Frame type + * @rx_rssi: RX rssi + * @buf: Buffer address + */ +struct p2p_rx_mgmt_frame { + uint32_t frame_len; + uint32_t rx_chan; + uint32_t vdev_id; + uint32_t frm_type; + uint32_t rx_rssi; + uint8_t buf[1]; +}; + +/** + * struct p2p_tx_cnf - tx confirm structure + * @vdev_id: Vdev id + * @action_cookie: TX cookie for this action frame + * @buf_len: Frame length + * @status: TX status + * @buf: Buffer address + */ +struct p2p_tx_cnf { + uint32_t vdev_id; + uint64_t action_cookie; + uint32_t buf_len; + uint32_t status; + uint8_t *buf; +}; + +/** + * struct p2p_mgmt_tx - p2p mgmt tx structure + * @vdev_id: Vdev id + * @chan: Chan for which this RoC has been requested + * @wait: Duration for the RoC + * @len: Length of tx buffer + * @no_cck: Required cck or not + * @dont_wait_for_ack: Wait for ack or not + * @off_chan: Off channel tx or not + * @buf: TX buffer + */ +struct p2p_mgmt_tx { + uint32_t vdev_id; + uint32_t chan; + uint32_t wait; + uint32_t len; + uint32_t no_cck; + uint32_t dont_wait_for_ack; + uint32_t off_chan; + const uint8_t *buf; +}; + +/** + * struct p2p_set_mac_filter + * @vdev_id: Vdev id + * @mac: mac addr + * @freq: frequency + * @set: set or clear + */ +struct p2p_set_mac_filter { + uint32_t vdev_id; + uint8_t mac[QDF_MAC_ADDR_SIZE]; + uint32_t freq; + bool set; +}; + +/** + * struct p2p_set_mac_filter_evt + * @vdev_id: Vdev id + * @status: target reported result of set mac addr filter + */ +struct p2p_set_mac_filter_evt { + uint32_t vdev_id; + uint32_t status; +}; + +/** + * struct p2p_ps_config + * @vdev_id: Vdev id + * @opp_ps: Opportunistic power save + * @ct_window: CT window + * @count: Count + * @duration: Duration + * @interval: Interval + * @single_noa_duration: Single shot noa duration + * @ps_selection: power save selection + */ +struct p2p_ps_config { + uint32_t vdev_id; + uint32_t opp_ps; + uint32_t ct_window; + uint32_t count; + uint32_t duration; + uint32_t interval; + uint32_t single_noa_duration; + uint32_t ps_selection; +}; + +/** + * struct p2p_lo_start - p2p listen offload start + * @vdev_id: Vdev id + * @ctl_flags: Control flag + * @freq: P2P listen frequency + * @period: Listen offload period + * @interval: Listen offload interval + * @count: Number listen offload intervals + * @dev_types_len: Device types length + * @probe_resp_len: Probe response template length + * @device_types: Device types + * @probe_resp_tmplt: Probe response template + */ +struct p2p_lo_start { + uint32_t vdev_id; + uint32_t ctl_flags; + uint32_t freq; + uint32_t period; + uint32_t interval; + uint32_t count; + uint32_t dev_types_len; + uint32_t probe_resp_len; + uint8_t *device_types; + uint8_t *probe_resp_tmplt; +}; + +/** + * struct p2p_lo_event + * @vdev_id: vdev id + * @reason_code: reason code + */ +struct p2p_lo_event { + uint32_t vdev_id; + uint32_t reason_code; +}; + +/** + * struct noa_descriptor - noa descriptor + * @type_count: 255: continuous schedule, 0: reserved + * @duration: Absent period duration in micro seconds + * @interval: Absent period interval in micro seconds + * @start_time: 32 bit tsf time when in starts + */ +struct noa_descriptor { + uint32_t type_count; + uint32_t duration; + uint32_t interval; + uint32_t start_time; +}; + +/** + * struct p2p_noa_info - p2p noa information + * @index: identifies instance of NOA su element + * @opps_ps: opps ps state of the AP + * @ct_window: ct window in TUs + * @vdev_id: vdev id + * @num_descriptors: number of NOA descriptors + * @noa_desc: noa descriptors + */ +struct p2p_noa_info { + uint32_t index; + uint32_t opps_ps; + uint32_t ct_window; + uint32_t vdev_id; + uint32_t num_desc; + struct noa_descriptor noa_desc[P2P_MAX_NOA_DESC]; +}; + +/** + * struct p2p_protocol_callbacks - callback to non-converged driver + * @is_mgmt_protected: func to get 11w mgmt protection status + */ +struct p2p_protocol_callbacks { + bool (*is_mgmt_protected)(uint32_t vdev_id, const uint8_t *peer_addr); +}; + +#endif /* _WLAN_P2P_PUBLIC_STRUCT_H_ */ diff --git a/p2p/dispatcher/inc/wlan_p2p_tgt_api.h b/p2p/dispatcher/inc/wlan_p2p_tgt_api.h new file mode 100644 index 0000000000..0ca2074989 --- /dev/null +++ b/p2p/dispatcher/inc/wlan_p2p_tgt_api.h @@ -0,0 +1,208 @@ +/* + * Copyright (c) 2017-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. + */ + +/** + * DOC: Contains p2p south bound interface definitions + */ + +#ifndef _WLAN_P2P_TGT_API_H_ +#define _WLAN_P2P_TGT_API_H_ + +#include +#include + +struct scan_event; +struct wlan_objmgr_psoc; +struct wlan_objmgr_peer; +struct p2p_noa_info; +struct p2p_lo_event; +struct mgmt_rx_event_params; +enum mgmt_frame_type; + +#ifdef FEATURE_P2P_LISTEN_OFFLOAD + +/** + * tgt_p2p_lo_event_cb() - Listen offload stop request + * @psoc: soc object + * @event_info: lo stop event buffer + * + * This function gets called from target interface. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS tgt_p2p_lo_event_cb(struct wlan_objmgr_psoc *psoc, + struct p2p_lo_event *event_info); + +/** + * tgt_p2p_register_lo_ev_handler() - register lo event + * @psoc: soc object + * + * p2p tgt api to register listen offload event handler. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS tgt_p2p_register_lo_ev_handler( + struct wlan_objmgr_psoc *psoc); + +/** + * tgt_p2p_unregister_lo_ev_handler() - unregister lo event + * @psoc: soc object + * + * p2p tgt api to unregister listen offload event handler. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS tgt_p2p_unregister_lo_ev_handler( + struct wlan_objmgr_psoc *psoc); +#else +static inline QDF_STATUS tgt_p2p_register_lo_ev_handler( + struct wlan_objmgr_psoc *psoc) +{ + return QDF_STATUS_SUCCESS; +} + +static inline QDF_STATUS tgt_p2p_unregister_lo_ev_handler( + struct wlan_objmgr_psoc *psoc) +{ + return QDF_STATUS_SUCCESS; +} +#endif + +/** + * tgt_p2p_register_macaddr_rx_filter_evt_handler() - register add mac rx + * filter status event + * @psoc: soc object + * @register: register or unregister + * + * p2p tgt api to register add mac rx filter status event + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS tgt_p2p_register_macaddr_rx_filter_evt_handler( + struct wlan_objmgr_psoc *psoc, bool register); + +/** + * tgt_p2p_register_noa_ev_handler() - register noa event + * @psoc: soc object + * + * p2p tgt api to register noa event handler. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS tgt_p2p_register_noa_ev_handler( + struct wlan_objmgr_psoc *psoc); + +/** + * tgt_p2p_unregister_noa_ev_handler() - unregister noa event + * @psoc: soc object + * + * p2p tgt api to unregister noa event handler. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS tgt_p2p_unregister_noa_ev_handler( + struct wlan_objmgr_psoc *psoc); + +/** + * tgt_p2p_scan_event_cb() - Callback for scan event + * @vdev: vdev object + * @event: event information + * @arg: registered arguments + * + * This function gets called from scan component when getting P2P + * scan event. + * + * Return: None + */ +void tgt_p2p_scan_event_cb(struct wlan_objmgr_vdev *vdev, + struct scan_event *event, void *arg); + +/** + * tgt_p2p_mgmt_download_comp_cb() - Callback for mgmt frame tx + * complete + * @context: tx context + * @buf: buffer address + * @free: need to free or not + * + * This function gets called from mgmt tx/rx component when mgmt + * frame tx complete. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS tgt_p2p_mgmt_download_comp_cb(void *context, + qdf_nbuf_t buf, bool free); + +/** + * tgt_p2p_mgmt_ota_comp_cb() - Callback for mgmt frame tx ack + * @context: tx context + * @buf: buffer address + * @status: tx status + * @tx_compl_params: tx complete parameters + * + * This function gets called from mgmt tx/rx component when getting + * mgmt frame tx ack. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS tgt_p2p_mgmt_ota_comp_cb(void *context, qdf_nbuf_t buf, + uint32_t status, void *tx_compl_params); + +/** + * tgt_p2p_mgmt_frame_rx_cb() - Callback for rx mgmt frame + * @psoc: soc context + * @peer: peer context + * @buf: rx buffer + * @mgmt_rx_params: mgmt rx parameters + * @frm_type: frame type + * + * This function gets called from mgmt tx/rx component when rx mgmt + * frame. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS tgt_p2p_mgmt_frame_rx_cb(struct wlan_objmgr_psoc *psoc, + struct wlan_objmgr_peer *peer, qdf_nbuf_t buf, + struct mgmt_rx_event_params *mgmt_rx_params, + enum mgmt_frame_type frm_type); +/** + * tgt_p2p_noa_event_cb() - Callback for noa event + * @psoc: soc object + * @event_info: noa event information + * + * This function gets called from target interface. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS tgt_p2p_noa_event_cb(struct wlan_objmgr_psoc *psoc, + struct p2p_noa_info *event_info); + +/** + * tgt_p2p_add_mac_addr_status_event_cb() - Callback for set mac addr filter evt + * @psoc: soc object + * @event_info: event information type of p2p_set_mac_filter_evt + * + * This function gets called from target interface. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS +tgt_p2p_add_mac_addr_status_event_cb( + struct wlan_objmgr_psoc *psoc, + struct p2p_set_mac_filter_evt *event_info); + +#endif /* _WLAN_P2P_TGT_API_H_ */ diff --git a/p2p/dispatcher/inc/wlan_p2p_ucfg_api.h b/p2p/dispatcher/inc/wlan_p2p_ucfg_api.h new file mode 100644 index 0000000000..da39720514 --- /dev/null +++ b/p2p/dispatcher/inc/wlan_p2p_ucfg_api.h @@ -0,0 +1,411 @@ +/* + * Copyright (c) 2017-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. + */ + +/** + * DOC: Contains p2p north bound interface definitions + */ + +#ifndef _WLAN_P2P_UCFG_API_H_ +#define _WLAN_P2P_UCFG_API_H_ + +#include + +struct wlan_objmgr_psoc; +struct p2p_roc_req; +struct p2p_event; +struct p2p_rx_mgmt_frame; +struct p2p_tx_cnf; +struct p2p_mgmt_tx; +struct p2p_ps_config; +struct p2p_lo_start; +struct p2p_lo_event; + +/** + * p2p_rx_callback() - Callback for rx mgmt frame + * @user_data: user data associated to this rx mgmt frame. + * @rx_frame: RX mgmt frame + * + * This callback will be used to give rx frames to hdd. + * + * Return: None + */ +typedef void (*p2p_rx_callback)(void *user_data, + struct p2p_rx_mgmt_frame *rx_frame); + +/** + * p2p_action_tx_cnf_callback() - Callback for tx confirmation + * @user_data: user data associated to this tx confirmation + * @tx_cnf: tx confirmation information + * + * This callback will be used to give tx mgmt frame confirmation to + * hdd. + * + * Return: None + */ +typedef void (*p2p_action_tx_cnf_callback)(void *user_data, + struct p2p_tx_cnf *tx_cnf); + +/** + * p2p_lo_event_callback() - Callback for listen offload event + * @user_data: user data associated to this lo event + * @p2p_lo_event: listen offload event information + * + * This callback will be used to give listen offload event to hdd. + * + * Return: None + */ +typedef void (*p2p_lo_event_callback)(void *user_data, + struct p2p_lo_event *p2p_lo_event); + +/** + * p2p_event_callback() - Callback for P2P event + * @user_data: user data associated to this p2p event + * @p2p_event: p2p event information + * + * This callback will be used to give p2p event to hdd. + * + * Return: None + */ +typedef void (*p2p_event_callback)(void *user_data, + struct p2p_event *p2p_event); + +/** + * struct p2p_start_param - p2p soc start parameters. Below callbacks + * will be registered by the HDD + * @rx_callback: Function pointer to hdd rx callback. This + * function will be used to give rx frames to hdd + * @rx_cb_data: RX callback user data + * @event_cb: Founction pointer to hdd p2p event callback. + * This function will be used to give p2p event + * to hdd + * @event_cb_data: Pointer to p2p event callback user data + * @tx_cnf_cb: Function pointer to hdd tx confirm callback. + * This function will be used to give tx confirm + * to hdd + * @tx_cnf_cb_data: Pointer to p2p tx confirm callback user data + * @lo_event_cb: Founction pointer to p2p listen offload + * callback. This function will be used to give + * listen offload stopped event to hdd + * @lo_event_cb_data: Pointer to p2p listen offload callback user data + */ +struct p2p_start_param { + p2p_rx_callback rx_cb; + void *rx_cb_data; + p2p_event_callback event_cb; + void *event_cb_data; + p2p_action_tx_cnf_callback tx_cnf_cb; + void *tx_cnf_cb_data; +#ifdef FEATURE_P2P_LISTEN_OFFLOAD + p2p_lo_event_callback lo_event_cb; + void *lo_event_cb_data; +#endif +}; + +/** + * ucfg_p2p_init() - P2P component initialization + * + * This function gets called when dispatcher initializing. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS ucfg_p2p_init(void); + +/** + * ucfg_p2p_deinit() - P2P component de-init + * + * This function gets called when dispatcher de-init. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS ucfg_p2p_deinit(void); + +/** + * ucfg_p2p_psoc_open() - Open P2P component + * @soc: soc context + * + * This function gets called when dispatcher opening. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS ucfg_p2p_psoc_open(struct wlan_objmgr_psoc *soc); + +/** + * ucfg_p2p_psoc_close() - Close P2P component + * @soc: soc context + * + * This function gets called when dispatcher closing. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS ucfg_p2p_psoc_close(struct wlan_objmgr_psoc *soc); + +/** + * ucfg_p2p_psoc_start() - Start P2P component + * @soc: soc context + * @req: P2P start parameters + * + * This function gets called when up layer starting up. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS ucfg_p2p_psoc_start(struct wlan_objmgr_psoc *soc, + struct p2p_start_param *req); + +/** + * ucfg_p2p_psoc_stop() - Stop P2P component + * @soc: soc context + * + * This function gets called when up layer exit. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS ucfg_p2p_psoc_stop(struct wlan_objmgr_psoc *soc); + +/** + * ucfg_p2p_roc_req() - Roc request + * @soc: soc context + * @roc_req: Roc request parameters + * @cookie: return cookie to caller + * + * This function delivers roc request to P2P component. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS ucfg_p2p_roc_req(struct wlan_objmgr_psoc *soc, + struct p2p_roc_req *roc_req, uint64_t *cookie); + +/** + * ucfg_p2p_roc_cancel_req() - Cancel roc request + * @soc: soc context + * @cookie: Find out the roc request by cookie + * + * This function delivers cancel roc request to P2P component. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS ucfg_p2p_roc_cancel_req(struct wlan_objmgr_psoc *soc, + uint64_t cookie); + +/** + * ucfg_p2p_cleanup_roc_by_vdev() - Cleanup roc request by vdev + * @vdev: pointer to vdev object + * + * This function call P2P API to cleanup roc request by vdev + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS ucfg_p2p_cleanup_roc_by_vdev(struct wlan_objmgr_vdev *vdev); + +/** + * ucfg_p2p_cleanup_roc_by_poc() - Cleanup roc request by psoc + * @psoc: pointer to psoc object + * + * This function call P2P API to cleanup roc request by psoc + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS ucfg_p2p_cleanup_roc_by_psoc(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_p2p_cleanup_tx_by_vdev() - Cleanup tx request by vdev + * @vdev: pointer to vdev object + * + * This function call P2P API to cleanup tx action frame request by vdev + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS ucfg_p2p_cleanup_tx_by_vdev(struct wlan_objmgr_vdev *vdev); + +/** + * ucfg_p2p_cleanup_tx_by_poc() - Cleanup tx request by psoc + * @psoc: pointer to psoc object + * + * This function call P2P API to cleanup tx action frame request by psoc + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS ucfg_p2p_cleanup_tx_by_psoc(struct wlan_objmgr_psoc *psoc); + +/** + * ucfg_p2p_mgmt_tx() - Mgmt frame tx request + * @soc: soc context + * @mgmt_frm: TX mgmt frame parameters + * @cookie: Return the cookie to caller + * + * This function delivers mgmt frame tx request to P2P component. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS ucfg_p2p_mgmt_tx(struct wlan_objmgr_psoc *soc, + struct p2p_mgmt_tx *mgmt_frm, uint64_t *cookie); + +/** + * ucfg_p2p_mgmt_tx_cancel() - Cancel mgmt frame tx request + * @soc: soc context + * @vdev: vdev object + * @cookie: Find out the mgmt tx request by cookie + * + * This function delivers cancel mgmt frame tx request request to P2P + * component. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS ucfg_p2p_mgmt_tx_cancel(struct wlan_objmgr_psoc *soc, + struct wlan_objmgr_vdev *vdev, uint64_t cookie); + +/** + * ucfg_p2p_set_ps() - P2P set power save + * @soc: soc context + * @ps_config: power save configure + * + * This function delivers p2p power save request to P2P component. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS ucfg_p2p_set_ps(struct wlan_objmgr_psoc *soc, + struct p2p_ps_config *ps_config); + +#ifdef FEATURE_P2P_LISTEN_OFFLOAD +/** + * ucfg_p2p_lo_start() - Listen offload start request + * @soc: soc context + * @p2p_lo_start: lo start parameters + * + * This function delivers listen offload start request to P2P + * component. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS ucfg_p2p_lo_start(struct wlan_objmgr_psoc *soc, + struct p2p_lo_start *p2p_lo_start); + +/** + * ucfg_p2p_lo_stop() - Listen offload stop request + * @soc: soc context + * @vdev_id: vdev id + * + * This function delivers listen offload stop request to P2P component. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS ucfg_p2p_lo_stop(struct wlan_objmgr_psoc *soc, + uint32_t vdev_id); +#endif + +/** + * p2p_peer_authorized() - Process peer authorized event + * @vdev: vdev structure to which peer is associated + * @mac_addr: peer mac address + * + * This function handles disables noa whenever a legacy station + * complete 4-way handshake after association. + * + * Return: void + */ +void p2p_peer_authorized(struct wlan_objmgr_vdev *vdev, uint8_t *mac_addr); + +/** + * ucfg_p2p_set_noa() - Disable/Enable NOA + * @soc: soc context + * @vdev_id: vdev id + * @disable_noa: TRUE - Disable NoA, FALSE - Enable NoA + * + * This function send wmi command to enable / disable NoA. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS ucfg_p2p_set_noa(struct wlan_objmgr_psoc *soc, + uint32_t vdev_id, bool disable_noa); + +/** + * ucfg_p2p_check_random_mac() - check random mac addr or not + * @soc: soc context + * @vdev_id: vdev id + * @random_mac_addr: mac addr to be checked + * + * This function check the input addr is random mac addr or not for vdev. + * + * Return: true if addr is random mac address else false. + */ +bool ucfg_p2p_check_random_mac(struct wlan_objmgr_psoc *soc, uint32_t vdev_id, + uint8_t *random_mac_addr); + +/** + * ucfg_p2p_register_callbacks() - register p2p callbacks + * @soc: soc context + * @cb_obj: p2p_protocol_callbacks struct + * + * This function registers lim callbacks to p2p components to provide + * protocol information. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS ucfg_p2p_register_callbacks(struct wlan_objmgr_psoc *soc, + struct p2p_protocol_callbacks *cb_obj); + +/** + * ucfg_p2p_status_scan() - Show P2P connection status when scanning + * @vdev: vdev context + * + * This function shows P2P connection status when scanning. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS ucfg_p2p_status_scan(struct wlan_objmgr_vdev *vdev); + +/** + * ucfg_p2p_status_connect() - Update P2P connection status + * @vdev: vdev context + * + * Updates P2P connection status by up layer when connecting. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS ucfg_p2p_status_connect(struct wlan_objmgr_vdev *vdev); + +/** + * ucfg_p2p_status_disconnect() - Update P2P connection status + * @vdev: vdev context + * + * Updates P2P connection status by up layer when disconnecting. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS ucfg_p2p_status_disconnect(struct wlan_objmgr_vdev *vdev); + +/** + * ucfg_p2p_status_start_bss() - Update P2P connection status + * @vdev: vdev context + * + * Updates P2P connection status by up layer when starting bss. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS ucfg_p2p_status_start_bss(struct wlan_objmgr_vdev *vdev); + +/** + * ucfg_p2p_status_stop_bss() - Update P2P connection status + * @vdev: vdev context + * + * Updates P2P connection status by up layer when stopping bss. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS ucfg_p2p_status_stop_bss(struct wlan_objmgr_vdev *vdev); + +#endif /* _WLAN_P2P_UCFG_API_H_ */ diff --git a/p2p/dispatcher/src/wlan_p2p_cfg.c b/p2p/dispatcher/src/wlan_p2p_cfg.c new file mode 100644 index 0000000000..490b95821e --- /dev/null +++ b/p2p/dispatcher/src/wlan_p2p_cfg.c @@ -0,0 +1,105 @@ +/* + * 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. + */ + +/** + * DOC: This file contains p2p configures interface definitions + */ + +#include +#include "wlan_p2p_public_struct.h" +#include "wlan_p2p_cfg_api.h" +#include "../../core/src/wlan_p2p_main.h" + +static inline struct p2p_soc_priv_obj * +wlan_psoc_get_p2p_object(struct wlan_objmgr_psoc *psoc) +{ + return wlan_objmgr_psoc_get_comp_private_obj(psoc, + WLAN_UMAC_COMP_P2P); +} + +QDF_STATUS +cfg_p2p_get_go_keepalive_period(struct wlan_objmgr_psoc *psoc, + uint32_t *period) +{ + struct p2p_soc_priv_obj *p2p_soc_obj; + + p2p_soc_obj = wlan_psoc_get_p2p_object(psoc); + if (!p2p_soc_obj) { + *period = 0; + p2p_err("p2p psoc null"); + return QDF_STATUS_E_INVAL; + } + + *period = p2p_soc_obj->param.go_keepalive_period; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +cfg_p2p_get_go_link_monitor_period(struct wlan_objmgr_psoc *psoc, + uint32_t *period) +{ + struct p2p_soc_priv_obj *p2p_soc_obj; + + p2p_soc_obj = wlan_psoc_get_p2p_object(psoc); + if (!p2p_soc_obj) { + *period = 0; + p2p_err("p2p psoc null"); + return QDF_STATUS_E_INVAL; + } + + *period = p2p_soc_obj->param.go_link_monitor_period; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +cfg_p2p_get_device_addr_admin(struct wlan_objmgr_psoc *psoc, + bool *enable) +{ + struct p2p_soc_priv_obj *p2p_soc_obj; + + p2p_soc_obj = wlan_psoc_get_p2p_object(psoc); + if (!p2p_soc_obj) { + *enable = false; + p2p_err("p2p psoc null"); + return QDF_STATUS_E_INVAL; + } + + *enable = p2p_soc_obj->param.p2p_device_addr_admin; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS +cfg_p2p_get_skip_dfs_channel_p2p_search(struct wlan_objmgr_psoc *psoc, + bool *enable) +{ + struct p2p_soc_priv_obj *p2p_soc_obj; + + p2p_soc_obj = wlan_psoc_get_p2p_object(psoc); + if (!p2p_soc_obj) { + *enable = false; + p2p_err("p2p psoc null"); + return QDF_STATUS_E_INVAL; + } + + *enable = p2p_soc_obj->param.skip_dfs_channel_p2p_search; + + return QDF_STATUS_SUCCESS; +} diff --git a/p2p/dispatcher/src/wlan_p2p_tgt_api.c b/p2p/dispatcher/src/wlan_p2p_tgt_api.c new file mode 100644 index 0000000000..d6ee448947 --- /dev/null +++ b/p2p/dispatcher/src/wlan_p2p_tgt_api.c @@ -0,0 +1,432 @@ +/* + * Copyright (c) 2017-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. + */ + +/** + * DOC: This file contains p2p south bound interface definitions + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "wlan_p2p_tgt_api.h" +#include "wlan_p2p_public_struct.h" +#include "../../core/src/wlan_p2p_main.h" +#include "../../core/src/wlan_p2p_roc.h" +#include "../../core/src/wlan_p2p_off_chan_tx.h" + +#define IEEE80211_FC0_TYPE_MASK 0x0c +#define P2P_NOISE_FLOOR_DBM_DEFAULT (-96) + +static inline struct wlan_lmac_if_p2p_tx_ops * +wlan_psoc_get_p2p_tx_ops(struct wlan_objmgr_psoc *psoc) +{ + return &(psoc->soc_cb.tx_ops.p2p); +} + +#ifdef FEATURE_P2P_LISTEN_OFFLOAD +QDF_STATUS tgt_p2p_register_lo_ev_handler( + struct wlan_objmgr_psoc *psoc) +{ + struct wlan_lmac_if_p2p_tx_ops *p2p_ops; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + p2p_ops = wlan_psoc_get_p2p_tx_ops(psoc); + if (p2p_ops && p2p_ops->reg_lo_ev_handler) { + status = p2p_ops->reg_lo_ev_handler(psoc, NULL); + p2p_debug("register lo event, status:%d", status); + } + + return status; +} + +QDF_STATUS tgt_p2p_unregister_lo_ev_handler( + struct wlan_objmgr_psoc *psoc) +{ + struct wlan_lmac_if_p2p_tx_ops *p2p_ops; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + p2p_ops = wlan_psoc_get_p2p_tx_ops(psoc); + if (p2p_ops && p2p_ops->unreg_lo_ev_handler) { + status = p2p_ops->unreg_lo_ev_handler(psoc, NULL); + p2p_debug("unregister lo event, status:%d", status); + } + + return status; +} + +QDF_STATUS tgt_p2p_lo_event_cb(struct wlan_objmgr_psoc *psoc, + struct p2p_lo_event *event_info) +{ + struct p2p_lo_stop_event *lo_stop_event; + struct scheduler_msg msg = {0}; + struct p2p_soc_priv_obj *p2p_soc_obj; + QDF_STATUS status; + + p2p_debug("soc:%pK, event_info:%pK", psoc, event_info); + + if (!psoc) { + p2p_err("psoc context passed is NULL"); + if (event_info) + qdf_mem_free(event_info); + return QDF_STATUS_E_INVAL; + } + + p2p_soc_obj = wlan_objmgr_psoc_get_comp_private_obj(psoc, + WLAN_UMAC_COMP_P2P); + if (!p2p_soc_obj) { + p2p_err("p2p soc object is NULL"); + if (event_info) + qdf_mem_free(event_info); + return QDF_STATUS_E_INVAL; + } + + if (!event_info) { + p2p_err("invalid lo stop event information"); + return QDF_STATUS_E_INVAL; + } + + lo_stop_event = qdf_mem_malloc(sizeof(*lo_stop_event)); + if (!lo_stop_event) { + p2p_err("Failed to allocate p2p lo stop event"); + qdf_mem_free(event_info); + return QDF_STATUS_E_NOMEM; + } + + lo_stop_event->p2p_soc_obj = p2p_soc_obj; + lo_stop_event->lo_event = event_info; + msg.type = P2P_EVENT_LO_STOPPED; + msg.bodyptr = lo_stop_event; + msg.callback = p2p_process_evt; + msg.flush_callback = p2p_event_flush_callback; + status = scheduler_post_message(QDF_MODULE_ID_P2P, + QDF_MODULE_ID_P2P, + QDF_MODULE_ID_TARGET_IF, + &msg); + if (QDF_IS_STATUS_ERROR(status)) { + qdf_mem_free(lo_stop_event->lo_event); + qdf_mem_free(lo_stop_event); + p2p_err("post msg fail:%d", status); + } + + return status; +} +#endif /* FEATURE_P2P_LISTEN_OFFLOAD */ + +QDF_STATUS +tgt_p2p_add_mac_addr_status_event_cb(struct wlan_objmgr_psoc *psoc, + struct p2p_set_mac_filter_evt *event_info) +{ + struct p2p_mac_filter_rsp *mac_filter_rsp; + struct scheduler_msg msg = {0}; + struct p2p_soc_priv_obj *p2p_soc_obj; + QDF_STATUS status; + + if (!psoc) { + p2p_err("random_mac:psoc context passed is NULL"); + return QDF_STATUS_E_INVAL; + } + if (!event_info) { + p2p_err("random_mac:invalid event_info"); + return QDF_STATUS_E_INVAL; + } + + p2p_soc_obj = wlan_objmgr_psoc_get_comp_private_obj( + psoc, WLAN_UMAC_COMP_P2P); + if (!p2p_soc_obj) { + p2p_err("random_mac:p2p soc object is NULL"); + return QDF_STATUS_E_INVAL; + } + + mac_filter_rsp = qdf_mem_malloc(sizeof(*mac_filter_rsp)); + if (!mac_filter_rsp) { + p2p_err("random_mac:Failed to allocate mac_filter_rsp"); + return QDF_STATUS_E_NOMEM; + } + + mac_filter_rsp->p2p_soc_obj = p2p_soc_obj; + mac_filter_rsp->vdev_id = event_info->vdev_id; + mac_filter_rsp->status = event_info->status; + + msg.type = P2P_EVENT_ADD_MAC_RSP; + msg.bodyptr = mac_filter_rsp; + msg.callback = p2p_process_evt; + status = scheduler_post_msg(QDF_MODULE_ID_TARGET_IF, &msg); + if (QDF_IS_STATUS_ERROR(status)) + qdf_mem_free(mac_filter_rsp); + + return status; +} + +QDF_STATUS tgt_p2p_register_macaddr_rx_filter_evt_handler( + struct wlan_objmgr_psoc *psoc, bool reg) +{ + struct wlan_lmac_if_p2p_tx_ops *p2p_ops; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + p2p_ops = wlan_psoc_get_p2p_tx_ops(psoc); + if (p2p_ops && p2p_ops->reg_mac_addr_rx_filter_handler) { + status = p2p_ops->reg_mac_addr_rx_filter_handler(psoc, reg); + p2p_debug("register mac addr rx filter event, register %d status:%d", + reg, status); + } + + return status; +} + +QDF_STATUS tgt_p2p_register_noa_ev_handler( + struct wlan_objmgr_psoc *psoc) +{ + struct wlan_lmac_if_p2p_tx_ops *p2p_ops; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + p2p_ops = wlan_psoc_get_p2p_tx_ops(psoc); + if (p2p_ops && p2p_ops->reg_noa_ev_handler) { + status = p2p_ops->reg_noa_ev_handler(psoc, NULL); + p2p_debug("register noa event, status:%d", status); + } + + return status; +} + +QDF_STATUS tgt_p2p_unregister_noa_ev_handler( + struct wlan_objmgr_psoc *psoc) +{ + struct wlan_lmac_if_p2p_tx_ops *p2p_ops; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + p2p_ops = wlan_psoc_get_p2p_tx_ops(psoc); + if (p2p_ops && p2p_ops->unreg_noa_ev_handler) { + status = p2p_ops->unreg_noa_ev_handler(psoc, NULL); + p2p_debug("unregister noa event, status:%d", status); + } + + return status; +} + +void tgt_p2p_scan_event_cb(struct wlan_objmgr_vdev *vdev, + struct scan_event *event, void *arg) +{ + p2p_scan_event_cb(vdev, event, arg); +} + +QDF_STATUS tgt_p2p_mgmt_download_comp_cb(void *context, + qdf_nbuf_t buf, bool free) +{ + p2p_debug("conext:%pK, buf:%pK, free:%d", context, + qdf_nbuf_data(buf), free); + + qdf_nbuf_free(buf); + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS tgt_p2p_mgmt_ota_comp_cb(void *context, qdf_nbuf_t buf, + uint32_t status, void *tx_compl_params) +{ + struct p2p_tx_conf_event *tx_conf_event; + struct scheduler_msg msg = {0}; + QDF_STATUS ret; + + p2p_debug("context:%pK, buf:%pK, status:%d, tx complete params:%pK", + context, buf, status, tx_compl_params); + + if (!context) { + p2p_err("invalid context"); + qdf_nbuf_free(buf); + return QDF_STATUS_E_INVAL; + } + + tx_conf_event = qdf_mem_malloc(sizeof(*tx_conf_event)); + if (!tx_conf_event) { + p2p_err("Failed to allocate tx cnf event"); + qdf_nbuf_free(buf); + return QDF_STATUS_E_NOMEM; + } + + tx_conf_event->status = status; + tx_conf_event->nbuf = buf; + tx_conf_event->p2p_soc_obj = (struct p2p_soc_priv_obj *)context; + msg.type = P2P_EVENT_MGMT_TX_ACK_CNF; + msg.bodyptr = tx_conf_event; + msg.callback = p2p_process_evt; + msg.flush_callback = p2p_event_flush_callback; + ret = scheduler_post_message(QDF_MODULE_ID_P2P, + QDF_MODULE_ID_P2P, + QDF_MODULE_ID_TARGET_IF, + &msg); + if (QDF_IS_STATUS_ERROR(ret)) { + qdf_mem_free(tx_conf_event); + qdf_nbuf_free(buf); + p2p_err("post msg fail:%d", status); + } + + return ret; +} + +QDF_STATUS tgt_p2p_mgmt_frame_rx_cb(struct wlan_objmgr_psoc *psoc, + struct wlan_objmgr_peer *peer, qdf_nbuf_t buf, + struct mgmt_rx_event_params *mgmt_rx_params, + enum mgmt_frame_type frm_type) +{ + struct p2p_rx_mgmt_frame *rx_mgmt; + struct p2p_rx_mgmt_event *rx_mgmt_event; + struct p2p_soc_priv_obj *p2p_soc_obj; + struct scheduler_msg msg = {0}; + struct wlan_objmgr_vdev *vdev; + uint32_t vdev_id; + uint8_t *pdata; + QDF_STATUS status; + + p2p_debug("psoc:%pK, peer:%pK, type:%d", psoc, peer, frm_type); + + if (!mgmt_rx_params) { + p2p_err("mgmt rx params is NULL"); + qdf_nbuf_free(buf); + return QDF_STATUS_E_INVAL; + } + + p2p_soc_obj = wlan_objmgr_psoc_get_comp_private_obj(psoc, + WLAN_UMAC_COMP_P2P); + if (!p2p_soc_obj) { + p2p_err("p2p ctx is NULL, drop this frame"); + qdf_nbuf_free(buf); + return QDF_STATUS_E_FAILURE; + } + + if (!peer) { + if (p2p_soc_obj->cur_roc_vdev_id == P2P_INVALID_VDEV_ID) { + p2p_err("vdev id of current roc invalid"); + qdf_nbuf_free(buf); + return QDF_STATUS_E_FAILURE; + } else { + vdev_id = p2p_soc_obj->cur_roc_vdev_id; + } + } else { + vdev = wlan_peer_get_vdev(peer); + if (!vdev) { + p2p_err("vdev is NULL in peer, drop this frame"); + qdf_nbuf_free(buf); + return QDF_STATUS_E_FAILURE; + } + vdev_id = wlan_vdev_get_id(vdev); + } + + rx_mgmt_event = qdf_mem_malloc(sizeof(*rx_mgmt_event)); + if (!rx_mgmt_event) { + p2p_err("Failed to allocate rx mgmt event"); + qdf_nbuf_free(buf); + return QDF_STATUS_E_NOMEM; + } + + rx_mgmt = qdf_mem_malloc(sizeof(*rx_mgmt) + + mgmt_rx_params->buf_len); + if (!rx_mgmt) { + p2p_err("Failed to allocate rx mgmt frame"); + qdf_nbuf_free(buf); + return QDF_STATUS_E_NOMEM; + } + + pdata = (uint8_t *)qdf_nbuf_data(buf); + rx_mgmt->frame_len = mgmt_rx_params->buf_len; + rx_mgmt->rx_chan = mgmt_rx_params->channel; + rx_mgmt->vdev_id = vdev_id; + rx_mgmt->frm_type = frm_type; + rx_mgmt->rx_rssi = mgmt_rx_params->snr + + P2P_NOISE_FLOOR_DBM_DEFAULT; + rx_mgmt_event->rx_mgmt = rx_mgmt; + rx_mgmt_event->p2p_soc_obj = p2p_soc_obj; + qdf_mem_copy(rx_mgmt->buf, pdata, mgmt_rx_params->buf_len); + msg.type = P2P_EVENT_RX_MGMT; + msg.bodyptr = rx_mgmt_event; + msg.callback = p2p_process_evt; + msg.flush_callback = p2p_event_flush_callback; + status = scheduler_post_message(QDF_MODULE_ID_P2P, + QDF_MODULE_ID_P2P, + QDF_MODULE_ID_TARGET_IF, + &msg); + if (QDF_IS_STATUS_ERROR(status)) { + qdf_mem_free(rx_mgmt_event->rx_mgmt); + qdf_mem_free(rx_mgmt_event); + p2p_err("post msg fail:%d", status); + } + qdf_nbuf_free(buf); + + return status; +} + +QDF_STATUS tgt_p2p_noa_event_cb(struct wlan_objmgr_psoc *psoc, + struct p2p_noa_info *event_info) +{ + struct p2p_noa_event *noa_event; + struct scheduler_msg msg = {0}; + struct p2p_soc_priv_obj *p2p_soc_obj; + QDF_STATUS status; + + p2p_debug("soc:%pK, event_info:%pK", psoc, event_info); + + if (!psoc) { + p2p_err("psoc context passed is NULL"); + if (event_info) + qdf_mem_free(event_info); + return QDF_STATUS_E_INVAL; + } + + p2p_soc_obj = wlan_objmgr_psoc_get_comp_private_obj(psoc, + WLAN_UMAC_COMP_P2P); + if (!p2p_soc_obj) { + p2p_err("p2p soc object is NULL"); + if (event_info) + qdf_mem_free(event_info); + return QDF_STATUS_E_INVAL; + } + + if (!event_info) { + p2p_err("invalid noa event information"); + return QDF_STATUS_E_INVAL; + } + + noa_event = qdf_mem_malloc(sizeof(*noa_event)); + if (!noa_event) { + p2p_err("Failed to allocate p2p noa event"); + qdf_mem_free(event_info); + return QDF_STATUS_E_NOMEM; + } + + noa_event->p2p_soc_obj = p2p_soc_obj; + noa_event->noa_info = event_info; + msg.type = P2P_EVENT_NOA; + msg.bodyptr = noa_event; + msg.callback = p2p_process_evt; + msg.flush_callback = p2p_event_flush_callback; + status = scheduler_post_message(QDF_MODULE_ID_P2P, + QDF_MODULE_ID_P2P, + QDF_MODULE_ID_TARGET_IF, + &msg); + if (QDF_IS_STATUS_ERROR(status)) { + qdf_mem_free(noa_event->noa_info); + qdf_mem_free(noa_event); + p2p_err("post msg fail:%d", status); + } + + return status; +} diff --git a/p2p/dispatcher/src/wlan_p2p_ucfg_api.c b/p2p/dispatcher/src/wlan_p2p_ucfg_api.c new file mode 100644 index 0000000000..f2a6c38aa6 --- /dev/null +++ b/p2p/dispatcher/src/wlan_p2p_ucfg_api.c @@ -0,0 +1,652 @@ +/* + * Copyright (c) 2017-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. + */ + +/** + * DOC: This file contains p2p north bound interface definitions + */ + +#include +#include +#include +#include +#include "wlan_p2p_public_struct.h" +#include "wlan_p2p_ucfg_api.h" +#include "../../core/src/wlan_p2p_main.h" +#include "../../core/src/wlan_p2p_roc.h" +#include "../../core/src/wlan_p2p_off_chan_tx.h" + +static inline struct wlan_lmac_if_p2p_tx_ops * +ucfg_p2p_psoc_get_tx_ops(struct wlan_objmgr_psoc *psoc) +{ + return &(psoc->soc_cb.tx_ops.p2p); +} + +/** + * is_p2p_ps_allowed() - If P2P power save is allowed or not + * @vdev: vdev object + * @id: umac component id + * + * This function returns TRUE if P2P power-save is allowed + * else returns FALSE. + * + * Return: bool + */ +static bool is_p2p_ps_allowed(struct wlan_objmgr_vdev *vdev, + enum wlan_umac_comp_id id) +{ + struct p2p_vdev_priv_obj *p2p_vdev_obj; + uint8_t is_p2pgo = 0; + + if (!vdev) { + p2p_err("vdev:%pK", vdev); + return true; + } + p2p_vdev_obj = wlan_objmgr_vdev_get_comp_private_obj(vdev, + WLAN_UMAC_COMP_P2P); + + if (wlan_vdev_mlme_get_opmode(vdev) == QDF_P2P_GO_MODE) + is_p2pgo = 1; + + if (!p2p_vdev_obj || !is_p2pgo) { + p2p_err("p2p_vdev_obj:%pK is_p2pgo:%u", + p2p_vdev_obj, is_p2pgo); + return false; + } + if (p2p_vdev_obj->non_p2p_peer_count && + p2p_vdev_obj->noa_status == false) { + p2p_debug("non_p2p_peer_count: %u, noa_status: %d", + p2p_vdev_obj->non_p2p_peer_count, + p2p_vdev_obj->noa_status); + return false; + } + + return true; +} + +QDF_STATUS ucfg_p2p_init(void) +{ + return p2p_component_init(); +} + +QDF_STATUS ucfg_p2p_deinit(void) +{ + return p2p_component_deinit(); +} + +QDF_STATUS ucfg_p2p_psoc_open(struct wlan_objmgr_psoc *soc) +{ + return p2p_psoc_object_open(soc); +} + +QDF_STATUS ucfg_p2p_psoc_close(struct wlan_objmgr_psoc *soc) +{ + return p2p_psoc_object_close(soc); +} + +QDF_STATUS ucfg_p2p_psoc_start(struct wlan_objmgr_psoc *soc, + struct p2p_start_param *req) +{ + return p2p_psoc_start(soc, req); +} + +QDF_STATUS ucfg_p2p_psoc_stop(struct wlan_objmgr_psoc *soc) +{ + return p2p_psoc_stop(soc); +} + +QDF_STATUS ucfg_p2p_roc_req(struct wlan_objmgr_psoc *soc, + struct p2p_roc_req *roc_req, uint64_t *cookie) +{ + struct scheduler_msg msg = {0}; + struct p2p_soc_priv_obj *p2p_soc_obj; + struct p2p_roc_context *roc_ctx; + QDF_STATUS status; + int32_t id; + + p2p_debug("soc:%pK, vdev_id:%d, chan:%d, phy_mode:%d, duration:%d", + soc, roc_req->vdev_id, roc_req->chan, + roc_req->phy_mode, roc_req->duration); + + 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; + } + + roc_ctx = qdf_mem_malloc(sizeof(*roc_ctx)); + if (!roc_ctx) { + p2p_err("failed to allocate p2p roc context"); + return QDF_STATUS_E_NOMEM; + } + + status = qdf_idr_alloc(&p2p_soc_obj->p2p_idr, roc_ctx, &id); + if (QDF_IS_STATUS_ERROR(status)) { + qdf_mem_free(roc_ctx); + p2p_err("failed to alloc idr, status %d", status); + return status; + } + + *cookie = (uint64_t)id; + roc_ctx->p2p_soc_obj = p2p_soc_obj; + roc_ctx->vdev_id = roc_req->vdev_id; + roc_ctx->chan = roc_req->chan; + roc_ctx->phy_mode = roc_req->phy_mode; + roc_ctx->duration = roc_req->duration; + roc_ctx->roc_state = ROC_STATE_IDLE; + roc_ctx->roc_type = USER_REQUESTED; + roc_ctx->id = id; + msg.type = P2P_ROC_REQ; + msg.bodyptr = roc_ctx; + msg.callback = p2p_process_cmd; + status = scheduler_post_message(QDF_MODULE_ID_HDD, + QDF_MODULE_ID_P2P, + QDF_MODULE_ID_OS_IF, + &msg); + if (QDF_IS_STATUS_ERROR(status)) { + qdf_mem_free(roc_ctx); + qdf_idr_remove(&p2p_soc_obj->p2p_idr, id); + p2p_err("post msg fail:%d", status); + } + p2p_debug("cookie = 0x%llx", *cookie); + + return status; +} + +QDF_STATUS ucfg_p2p_roc_cancel_req(struct wlan_objmgr_psoc *soc, + uint64_t cookie) +{ + struct scheduler_msg msg = {0}; + struct p2p_soc_priv_obj *p2p_soc_obj; + struct cancel_roc_context *cancel_roc; + void *roc_ctx = NULL; + QDF_STATUS status; + + p2p_debug("soc:%pK, cookie:0x%llx", soc, cookie); + + 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 context is NULL"); + return QDF_STATUS_E_FAILURE; + } + + status = qdf_idr_find(&p2p_soc_obj->p2p_idr, + cookie, &roc_ctx); + if (QDF_IS_STATUS_ERROR(status)) { + p2p_err("invalid id"); + return QDF_STATUS_E_INVAL; + } + + cancel_roc = qdf_mem_malloc(sizeof(*cancel_roc)); + if (!cancel_roc) { + p2p_err("failed to allocate cancel p2p roc"); + return QDF_STATUS_E_NOMEM; + } + + cancel_roc->p2p_soc_obj = p2p_soc_obj; + cancel_roc->cookie = (uintptr_t)roc_ctx; + msg.type = P2P_CANCEL_ROC_REQ; + msg.bodyptr = cancel_roc; + msg.callback = p2p_process_cmd; + status = scheduler_post_message(QDF_MODULE_ID_HDD, + QDF_MODULE_ID_P2P, + QDF_MODULE_ID_OS_IF, + &msg); + + if (QDF_IS_STATUS_ERROR(status)) { + qdf_mem_free(cancel_roc); + p2p_err("post msg fail:%d", status); + } + + return status; +} + +QDF_STATUS ucfg_p2p_cleanup_roc_by_vdev(struct wlan_objmgr_vdev *vdev) +{ + struct p2p_soc_priv_obj *p2p_soc_obj; + struct wlan_objmgr_psoc *psoc; + + p2p_debug("vdev:%pK", vdev); + + if (!vdev) { + p2p_err("null vdev"); + return QDF_STATUS_E_INVAL; + } + + psoc = wlan_vdev_get_psoc(vdev); + if (!psoc) { + p2p_err("null psoc"); + return QDF_STATUS_E_INVAL; + } + + p2p_soc_obj = wlan_objmgr_psoc_get_comp_private_obj(psoc, + WLAN_UMAC_COMP_P2P); + if (!p2p_soc_obj) { + p2p_err("p2p soc context is NULL"); + return QDF_STATUS_E_FAILURE; + } + + return p2p_cleanup_roc_sync(p2p_soc_obj, vdev); +} + +QDF_STATUS ucfg_p2p_cleanup_roc_by_psoc(struct wlan_objmgr_psoc *psoc) +{ + struct p2p_soc_priv_obj *obj; + + if (!psoc) { + p2p_err("null psoc"); + return QDF_STATUS_E_INVAL; + } + + obj = wlan_objmgr_psoc_get_comp_private_obj(psoc, WLAN_UMAC_COMP_P2P); + if (!obj) { + p2p_err("null p2p soc obj"); + return QDF_STATUS_E_FAILURE; + } + + return p2p_cleanup_roc_sync(obj, NULL); +} + +QDF_STATUS ucfg_p2p_cleanup_tx_by_vdev(struct wlan_objmgr_vdev *vdev) +{ + struct p2p_soc_priv_obj *obj; + struct wlan_objmgr_psoc *psoc; + + if (!vdev) { + p2p_err("null vdev"); + return QDF_STATUS_E_INVAL; + } + + psoc = wlan_vdev_get_psoc(vdev); + if (!psoc) { + p2p_err("null psoc"); + return QDF_STATUS_E_INVAL; + } + + obj = wlan_objmgr_psoc_get_comp_private_obj(psoc, WLAN_UMAC_COMP_P2P); + if (!obj) { + p2p_err("null p2p soc obj"); + return QDF_STATUS_E_FAILURE; + } + p2p_del_all_rand_mac_vdev(vdev); + + return p2p_cleanup_tx_sync(obj, vdev); +} + +QDF_STATUS ucfg_p2p_cleanup_tx_by_psoc(struct wlan_objmgr_psoc *psoc) +{ + struct p2p_soc_priv_obj *obj; + + if (!psoc) { + p2p_err("null psoc"); + return QDF_STATUS_E_INVAL; + } + + obj = wlan_objmgr_psoc_get_comp_private_obj(psoc, WLAN_UMAC_COMP_P2P); + if (!obj) { + p2p_err("null p2p soc obj"); + return QDF_STATUS_E_FAILURE; + } + p2p_del_all_rand_mac_soc(psoc); + + return p2p_cleanup_tx_sync(obj, NULL); +} + +QDF_STATUS ucfg_p2p_mgmt_tx(struct wlan_objmgr_psoc *soc, + struct p2p_mgmt_tx *mgmt_frm, uint64_t *cookie) +{ + struct scheduler_msg msg = {0}; + struct p2p_soc_priv_obj *p2p_soc_obj; + struct tx_action_context *tx_action; + QDF_STATUS status; + int32_t id; + + p2p_debug("soc:%pK, vdev_id:%d, chan:%d, wait:%d, buf_len:%d, cck:%d, no ack:%d, off chan:%d", + soc, mgmt_frm->vdev_id, mgmt_frm->chan, + mgmt_frm->wait, mgmt_frm->len, mgmt_frm->no_cck, + mgmt_frm->dont_wait_for_ack, mgmt_frm->off_chan); + + 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 context is NULL"); + return QDF_STATUS_E_FAILURE; + } + + tx_action = qdf_mem_malloc(sizeof(*tx_action)); + if (!tx_action) { + p2p_err("Failed to allocate tx action context"); + return QDF_STATUS_E_NOMEM; + } + + /* return cookie just for ota ack frames */ + if (mgmt_frm->dont_wait_for_ack) + id = 0; + else { + status = qdf_idr_alloc(&p2p_soc_obj->p2p_idr, + tx_action, &id); + if (QDF_IS_STATUS_ERROR(status)) { + qdf_mem_free(tx_action); + p2p_err("failed to alloc idr, status :%d", status); + return status; + } + } + + *cookie = (uint64_t)id; + tx_action->p2p_soc_obj = p2p_soc_obj; + tx_action->vdev_id = mgmt_frm->vdev_id; + tx_action->chan = mgmt_frm->chan; + tx_action->duration = mgmt_frm->wait; + tx_action->buf_len = mgmt_frm->len; + tx_action->no_cck = mgmt_frm->no_cck; + tx_action->no_ack = mgmt_frm->dont_wait_for_ack; + tx_action->off_chan = mgmt_frm->off_chan; + tx_action->buf = qdf_mem_malloc(tx_action->buf_len); + if (!(tx_action->buf)) { + p2p_err("Failed to allocate buffer for action frame"); + qdf_mem_free(tx_action); + return QDF_STATUS_E_NOMEM; + } + qdf_mem_copy(tx_action->buf, mgmt_frm->buf, tx_action->buf_len); + tx_action->nbuf = NULL; + tx_action->id = id; + + p2p_rand_mac_tx(tx_action); + + msg.type = P2P_MGMT_TX; + msg.bodyptr = tx_action; + msg.callback = p2p_process_cmd; + msg.flush_callback = p2p_msg_flush_callback; + status = scheduler_post_message(QDF_MODULE_ID_HDD, + QDF_MODULE_ID_P2P, + QDF_MODULE_ID_OS_IF, + &msg); + if (QDF_IS_STATUS_ERROR(status)) { + if (id) + qdf_idr_remove(&p2p_soc_obj->p2p_idr, id); + qdf_mem_free(tx_action->buf); + qdf_mem_free(tx_action); + p2p_err("post msg fail:%d", status); + } + + return status; +} + +QDF_STATUS ucfg_p2p_mgmt_tx_cancel(struct wlan_objmgr_psoc *soc, + struct wlan_objmgr_vdev *vdev, uint64_t cookie) +{ + struct scheduler_msg msg = {0}; + struct p2p_soc_priv_obj *p2p_soc_obj; + struct cancel_roc_context *cancel_tx; + void *tx_ctx; + QDF_STATUS status; + + p2p_debug("soc:%pK, cookie:0x%llx", soc, cookie); + + 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 context is NULL"); + return QDF_STATUS_E_FAILURE; + } + + status = qdf_idr_find(&p2p_soc_obj->p2p_idr, + (int32_t)cookie, &tx_ctx); + if (QDF_IS_STATUS_ERROR(status)) { + p2p_debug("invalid id"); + return QDF_STATUS_E_INVAL; + } + p2p_del_random_mac(soc, wlan_vdev_get_id(vdev), cookie, 20); + + cancel_tx = qdf_mem_malloc(sizeof(*cancel_tx)); + if (!cancel_tx) { + p2p_err("Failed to allocate cancel p2p roc"); + return QDF_STATUS_E_NOMEM; + } + + cancel_tx->p2p_soc_obj = p2p_soc_obj; + cancel_tx->cookie = (uintptr_t)tx_ctx; + msg.type = P2P_MGMT_TX_CANCEL; + msg.bodyptr = cancel_tx; + msg.callback = p2p_process_cmd; + status = scheduler_post_message(QDF_MODULE_ID_HDD, + QDF_MODULE_ID_P2P, + QDF_MODULE_ID_OS_IF, + &msg); + if (QDF_IS_STATUS_ERROR(status)) { + qdf_mem_free(cancel_tx); + p2p_err("post msg fail: %d", status); + } + + return status; +} + +bool ucfg_p2p_check_random_mac(struct wlan_objmgr_psoc *soc, uint32_t vdev_id, + uint8_t *random_mac_addr) +{ + return p2p_check_random_mac(soc, vdev_id, random_mac_addr); +} + +QDF_STATUS ucfg_p2p_set_ps(struct wlan_objmgr_psoc *soc, + struct p2p_ps_config *ps_config) +{ + struct wlan_lmac_if_p2p_tx_ops *p2p_ops; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + uint16_t obj_id; + struct wlan_objmgr_vdev *vdev; + struct p2p_ps_config go_ps_config; + + p2p_debug("soc:%pK, vdev_id:%d, opp_ps:%d, ct_window:%d, count:%d, duration:%d, duration:%d, ps_selection:%d", + soc, ps_config->vdev_id, ps_config->opp_ps, + ps_config->ct_window, ps_config->count, + ps_config->duration, ps_config->single_noa_duration, + ps_config->ps_selection); + + if (!soc) { + p2p_err("psoc context passed is NULL"); + return QDF_STATUS_E_INVAL; + } + + for (obj_id = 0; obj_id < WLAN_UMAC_PSOC_MAX_VDEVS; obj_id++) { + + vdev = wlan_objmgr_get_vdev_by_id_from_psoc(soc, obj_id, + WLAN_P2P_ID); + if (vdev) { + if (is_p2p_ps_allowed(vdev, WLAN_UMAC_COMP_P2P)) { + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); + break; + } + wlan_objmgr_vdev_release_ref(vdev, WLAN_P2P_ID); + p2p_debug("skip p2p set ps vdev %d, NoA is disabled as legacy STA is connected to GO.", + obj_id); + } + } + if (obj_id >= WLAN_UMAC_PSOC_MAX_VDEVS) { + p2p_debug("No GO found!"); + return QDF_STATUS_E_INVAL; + } + go_ps_config = *ps_config; + go_ps_config.vdev_id = obj_id; + + p2p_ops = ucfg_p2p_psoc_get_tx_ops(soc); + if (p2p_ops->set_ps) { + status = p2p_ops->set_ps(soc, &go_ps_config); + p2p_debug("p2p set ps vdev %d, status:%d", obj_id, status); + } + + return status; +} + +#ifdef FEATURE_P2P_LISTEN_OFFLOAD +QDF_STATUS ucfg_p2p_lo_start(struct wlan_objmgr_psoc *soc, + struct p2p_lo_start *p2p_lo_start) +{ + struct wlan_lmac_if_p2p_tx_ops *p2p_ops; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + p2p_debug("soc:%pK, vdev_id:%d, ctl_flags:%d, freq:%d, period:%d, interval:%d, count:%d, dev_types_len:%d, probe_resp_len:%d, device_types:%pK, probe_resp_tmplt:%pK", + soc, p2p_lo_start->vdev_id, p2p_lo_start->ctl_flags, + p2p_lo_start->freq, p2p_lo_start->period, + p2p_lo_start->interval, p2p_lo_start->count, + p2p_lo_start->dev_types_len, p2p_lo_start->probe_resp_len, + p2p_lo_start->device_types, p2p_lo_start->probe_resp_tmplt); + + if (!soc) { + p2p_err("psoc context passed is NULL"); + return QDF_STATUS_E_INVAL; + } + + p2p_ops = ucfg_p2p_psoc_get_tx_ops(soc); + if (p2p_ops->lo_start) { + status = p2p_ops->lo_start(soc, p2p_lo_start); + p2p_debug("p2p lo start, status:%d", status); + } + + return status; +} + +QDF_STATUS ucfg_p2p_lo_stop(struct wlan_objmgr_psoc *soc, + uint32_t vdev_id) +{ + struct wlan_lmac_if_p2p_tx_ops *p2p_ops; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + p2p_debug("soc:%pK, vdev_id:%d", soc, vdev_id); + + if (!soc) { + p2p_err("psoc context passed is NULL"); + return QDF_STATUS_E_INVAL; + } + + p2p_ops = ucfg_p2p_psoc_get_tx_ops(soc); + if (p2p_ops->lo_stop) { + status = p2p_ops->lo_stop(soc, vdev_id); + p2p_debug("p2p lo stop, status:%d", status); + } + + return status; +} +#endif + +QDF_STATUS ucfg_p2p_set_noa(struct wlan_objmgr_psoc *soc, + uint32_t vdev_id, bool disable_noa) +{ + struct wlan_lmac_if_p2p_tx_ops *p2p_ops; + QDF_STATUS status = QDF_STATUS_E_INVAL; + + p2p_ops = ucfg_p2p_psoc_get_tx_ops(soc); + if (p2p_ops->set_noa) { + status = p2p_ops->set_noa(soc, vdev_id, disable_noa); + p2p_debug("p2p set noa, status:%d", status); + } + + return status; +} + +QDF_STATUS ucfg_p2p_register_callbacks(struct wlan_objmgr_psoc *soc, + struct p2p_protocol_callbacks *cb_obj) +{ + struct p2p_soc_priv_obj *p2p_soc_obj; + + if (!soc || !cb_obj) { + p2p_err("psoc %pM cb_obj %pM context passed is NULL", soc, + cb_obj); + 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->p2p_cb = *cb_obj; + + return QDF_STATUS_SUCCESS; +} + +QDF_STATUS ucfg_p2p_status_scan(struct wlan_objmgr_vdev *vdev) +{ + if (!vdev) { + p2p_err("vdev is NULL"); + return QDF_STATUS_E_INVAL; + } + + return p2p_status_scan(vdev); +} + +QDF_STATUS ucfg_p2p_status_connect(struct wlan_objmgr_vdev *vdev) +{ + if (!vdev) { + p2p_err("vdev is NULL"); + return QDF_STATUS_E_INVAL; + } + + return p2p_status_connect(vdev); +} + +QDF_STATUS ucfg_p2p_status_disconnect(struct wlan_objmgr_vdev *vdev) +{ + if (!vdev) { + p2p_err("vdev is NULL"); + return QDF_STATUS_E_INVAL; + } + + return p2p_status_disconnect(vdev); +} + +QDF_STATUS ucfg_p2p_status_start_bss(struct wlan_objmgr_vdev *vdev) +{ + if (!vdev) { + p2p_err("vdev is NULL"); + return QDF_STATUS_E_INVAL; + } + + return p2p_status_start_bss(vdev); +} + +QDF_STATUS ucfg_p2p_status_stop_bss(struct wlan_objmgr_vdev *vdev) +{ + if (!vdev) { + p2p_err("vdev is NULL"); + return QDF_STATUS_E_INVAL; + } + + return p2p_status_stop_bss(vdev); +}