From 47276d678541400efab69b803998efe84822270c Mon Sep 17 00:00:00 2001 From: Wu Gao Date: Wed, 22 Mar 2017 13:18:15 +0800 Subject: [PATCH] qcacmn: mgmt frame txrx Modify P2P IE and tx mgmt frame. Handles tx confirm and rx frame events. Change-Id: I0c0ada2e12ee5ebdd3e8d7b7a6f2bd2af4357548 CRs-Fixed: 2015297 --- core/src/wlan_p2p_main.c | 1 + core/src/wlan_p2p_main.h | 3 + core/src/wlan_p2p_off_chan_tx.c | 1527 +++++++++++++++++++++++ core/src/wlan_p2p_off_chan_tx.h | 148 ++- core/src/wlan_p2p_roc.c | 1 + dispatcher/inc/wlan_p2p_public_struct.h | 2 + dispatcher/src/wlan_p2p_tgt_api.c | 133 ++ dispatcher/src/wlan_p2p_ucfg_api.c | 85 ++ 8 files changed, 1896 insertions(+), 4 deletions(-) diff --git a/core/src/wlan_p2p_main.c b/core/src/wlan_p2p_main.c index 25e53727ab5d..96d472b510b8 100644 --- a/core/src/wlan_p2p_main.c +++ b/core/src/wlan_p2p_main.c @@ -579,6 +579,7 @@ QDF_STATUS p2p_psoc_stop(struct wlan_objmgr_psoc *soc) } /* clean up queue of p2p psoc private object */ + p2p_cleanup_tx_queue(p2p_soc_obj); p2p_cleanup_roc_queue(p2p_soc_obj); /* unrgister scan request id*/ diff --git a/core/src/wlan_p2p_main.h b/core/src/wlan_p2p_main.h index 7b2e0056563b..7f1ff9539a76 100644 --- a/core/src/wlan_p2p_main.h +++ b/core/src/wlan_p2p_main.h @@ -53,6 +53,7 @@ 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 @@ -88,10 +89,12 @@ enum p2p_event_type { * struct p2p_tx_conf_event - p2p tx confirm event * @p2p_soc_obj: p2p soc private object * @tx_cnf: p2p tx confirm structure + * @tx_ctx: tx context */ struct p2p_tx_conf_event { struct p2p_soc_priv_obj *p2p_soc_obj; struct p2p_tx_cnf *tx_cnf; + struct tx_action_context *tx_ctx; }; /** diff --git a/core/src/wlan_p2p_off_chan_tx.c b/core/src/wlan_p2p_off_chan_tx.c index 99ceaff00ab1..45208cc1c158 100644 --- a/core/src/wlan_p2p_off_chan_tx.c +++ b/core/src/wlan_p2p_off_chan_tx.c @@ -20,28 +20,1555 @@ * DOC: This file contains off channel tx API definitions */ +#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_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 tQDF_ADAPTER_MODE 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; + } + + wlan_vdev_obj_lock(vdev); + mode = wlan_vdev_mlme_get_opmode(vdev); + wlan_vdev_obj_unlock(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 uint8_t *p2p_get_p2pie_ptr(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 uint8_t *p2p_get_p2pie_from_probe_rsp( + struct tx_action_context *tx_ctx) +{ + uint8_t *ie; + uint8_t *p2p_ie; + uint8_t *tmp_p2p_ie; + uint16_t ie_len; + + 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 uint8_t *p2p_get_presence_noa_attr(uint8_t *pies, int length) +{ + int left = length; + uint8_t *ptr = pies; + uint8_t elem_id; + uint16_t elem_len; + + p2p_debug("pies:%p, 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:%p, 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 tQDF_ADAPTER_MODE 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; + } + + wlan_vdev_obj_lock(vdev); + mode = wlan_vdev_mlme_get_opmode(vdev); + wlan_vdev_obj_unlock(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, 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=%p 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; + + psoc = tx_ctx->p2p_soc_obj->soc; + + wh = (struct wlan_frame_hdr *)tx_ctx->buf; + mac_addr = wh->i_addr1; + peer = wlan_objmgr_get_peer(psoc, mac_addr, WLAN_P2P_ID); + if (!peer) { + mac_addr = wh->i_addr2; + peer = wlan_objmgr_get_peer(psoc, mac_addr, + 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_set_protected_bit() - set protected bit + * @tx_ctx: tx context + * @frame: pointer to frame + * + * This function sets protected bit of this mgmt frame + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_set_protected_bit( + struct tx_action_context *tx_ctx, void *frame) +{ + return QDF_STATUS_E_INVAL; +} + +/** + * 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_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 + * @tx_ctx: tx context + * + * This function gets frame information from packet. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +static QDF_STATUS p2p_get_frame_info(struct tx_action_context *tx_ctx) +{ + uint8_t type; + uint8_t sub_type; + uint8_t action_type; + uint8_t *buf = tx_ctx->buf; + struct p2p_frame_info *frame_info = &(tx_ctx->frame_info); + + type = P2P_GET_TYPE_FRM_FC(buf[0]); + sub_type = P2P_GET_SUBTYPE_FRM_FC(buf[0]); + p2p_init_frame_info(frame_info); + 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 (buf[0] == P2P_PUBLIC_ACTION_FRAME && + buf[1] == P2P_PUBLIC_ACTION_VENDOR_SPECIFIC && + !qdf_mem_cmp(&buf[2], P2P_OUI, P2P_OUI_SIZE)) { + buf = tx_ctx->buf + + P2P_PUBLIC_ACTION_FRAME_TYPE_OFFSET; + action_type = buf[0]; + if (action_type >= P2P_PUBLIC_ACTION_NOT_SUPPORT) + frame_info->public_action_type = + P2P_PUBLIC_ACTION_NOT_SUPPORT; + else + frame_info->public_action_type = action_type; + } else if (buf[0] == P2P_ACTION_VENDOR_SPECIFIC_CATEGORY && + !qdf_mem_cmp(&buf[1], P2P_OUI, P2P_OUI_SIZE)) { + buf = tx_ctx->buf + + P2P_ACTION_FRAME_TYPE_OFFSET; + action_type = buf[0]; + 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; +} + +/** + * 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, + void **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 = (uintptr_t)tx_ctx; + + 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:%p, vdev_id:%d, action_cookie:%llx, len:%d, status:%d, buf:%p", + p2p_soc_obj->soc, tx_cnf.vdev_id, + tx_cnf.action_cookie, tx_cnf.buf_len, + tx_cnf.status, tx_cnf.buf); + + 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, void *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; + struct wlan_objmgr_psoc *psoc; + void *mac_addr; + + 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; + peer = wlan_objmgr_get_peer(psoc, mac_addr, WLAN_MGMT_NB_ID); + if (!peer) { + mac_addr = wh->i_addr2; + peer = wlan_objmgr_get_peer(psoc, mac_addr, + WLAN_MGMT_NB_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); + + status = wlan_mgmt_txrx_mgmt_frame_tx(peer, tx_ctx, + (qdf_nbuf_t)packet, + tx_comp_cb, tx_ota_comp_cb, + WLAN_UMAC_COMP_P2P, &mgmt_param); + + 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; + tx_ctx->roc_cookie = (uintptr_t)roc_ctx; + + p2p_debug("create roc request for off channel tx, tx ctx:%p, roc ctx:%p", + 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"); + qdf_mem_free(roc_ctx); + 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 *tmp, *pos; + *is_roc_q = false; + *is_ack_q = false; + + p2p_debug("Start to find tx ctx, p2p soc_obj:%p, cookie:%llx", + p2p_soc_obj, cookie); + + list_for_each_safe(pos, tmp, &p2p_soc_obj->tx_q_roc.anchor) { + cur_tx_ctx = + list_entry(pos, 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; + } + } + + list_for_each_safe(pos, tmp, &p2p_soc_obj->tx_q_ack.anchor) { + cur_tx_ctx = + list_entry(pos, 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; + } + } + + return NULL; +} + +/** + * p2p_find_tx_ctx_by_roc() - find tx context by roc + * @p2p_soc_obj: p2p soc object + * @cookie: cookie to roc 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 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, + bool *is_roc_q, bool *is_ack_q) +{ + struct tx_action_context *cur_tx_ctx; + qdf_list_node_t *tmp, *pos; + *is_roc_q = false; + *is_ack_q = false; + + p2p_debug("Start to find tx ctx, p2p soc_obj:%p, cookie:%llx", + p2p_soc_obj, cookie); + + list_for_each_safe(pos, tmp, &p2p_soc_obj->tx_q_roc.anchor) { + cur_tx_ctx = + list_entry(pos, struct tx_action_context, node); + if (cur_tx_ctx->roc_cookie == cookie) { + *is_roc_q = true; + p2p_debug("find tx ctx, cookie:%llx", cookie); + return cur_tx_ctx; + } + } + + list_for_each_safe(pos, tmp, &p2p_soc_obj->tx_q_ack.anchor) { + cur_tx_ctx = + list_entry(pos, struct tx_action_context, node); + if (cur_tx_ctx->roc_cookie == cookie) { + *is_ack_q = true; + p2p_debug("find tx ctx, cookie:%llx", cookie); + return cur_tx_ctx; + } + } + + 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, %p", 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_err("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 roc queue, status:%d", + status); + + return status; +} + +/** + * 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:%p", 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"); + + 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"); + } + + 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_debug("pdata:%p", pdata); + + if (!tx_ctx) { + p2p_err("invalid tx context"); + 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:%p", 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:%p", 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; +} + +/** + * 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; + void *packet; + QDF_STATUS status; + uint8_t noa_len = 0; + uint8_t noa_stream[P2P_NOA_STREAM_ARR_SIZE]; + uint8_t orig_len = 0; + uint8_t *ie; + uint8_t ie_len; + uint8_t *p2p_ie = NULL; + 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 = 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 = 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, + (void **)&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) + && (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); + } + + p2p_set_protected_bit(tx_ctx, frame); + 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; +} + +void p2p_dump_tx_queue(struct p2p_soc_priv_obj *p2p_soc_obj) +{ + struct tx_action_context *tx_ctx; + qdf_list_node_t *tmp, *pos; + + p2p_debug("dump tx queue wait for roc, p2p soc obj:%p, size:%d", + p2p_soc_obj, qdf_list_size(&p2p_soc_obj->tx_q_roc)); + list_for_each_safe(pos, tmp, &p2p_soc_obj->tx_q_roc.anchor) { + tx_ctx = list_entry(pos, struct tx_action_context, + node); + p2p_debug("p2p soc object:%p, tx ctx:%p, vdev_id:%d, scan_id:%d, roc_cookie:%llx, chan:%d, buf:%p, 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); + } + + p2p_debug("dump tx queue wait for ack, size:%d", + qdf_list_size(&p2p_soc_obj->tx_q_ack)); + list_for_each_safe(pos, tmp, &p2p_soc_obj->tx_q_ack.anchor) { + tx_ctx = list_entry(pos, struct tx_action_context, + node); + p2p_debug("p2p soc object:%p, tx_ctx:%p, vdev_id:%d, scan_id:%d, roc_cookie:%llx, chan:%d, buf:%p, 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); + } +} + +QDF_STATUS p2p_ready_to_tx_frame(struct p2p_soc_priv_obj *p2p_soc_obj, + uint64_t cookie) +{ + bool is_roc_q = false; + bool is_ack_q = false; + struct tx_action_context *cur_tx_ctx; + QDF_STATUS status = QDF_STATUS_E_FAILURE; + + cur_tx_ctx = p2p_find_tx_ctx_by_roc(p2p_soc_obj, cookie, + &is_roc_q, &is_ack_q); + p2p_debug("tx_ctx:%p, is_roc_q:%d, is_ack_q:%d", cur_tx_ctx, + is_roc_q, is_ack_q); + if (cur_tx_ctx) { + if (is_roc_q) { + 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); + } + } + if (is_ack_q) { + p2p_err("Already sent and waiting for ack"); + status = QDF_STATUS_SUCCESS; + } + } else { + p2p_err("Failed to find tx ctx by cookie, cookie %llx", + cookie); + status = QDF_STATUS_E_INVAL; + } + + return status; +} + +QDF_STATUS p2p_cleanup_tx_queue(struct p2p_soc_priv_obj *p2p_soc_obj) +{ + QDF_STATUS status = QDF_STATUS_E_FAILURE; + struct tx_action_context *curr_tx_ctx; + qdf_list_node_t *tmp, *pos; + + p2p_debug("clean up tx queue wait for roc, size:%d", + qdf_list_size(&p2p_soc_obj->tx_q_roc)); + list_for_each_safe(pos, tmp, &p2p_soc_obj->tx_q_roc.anchor) { + curr_tx_ctx = list_entry(pos, + struct tx_action_context, node); + status = qdf_list_remove_node(&p2p_soc_obj->tx_q_roc, + (qdf_list_node_t *)curr_tx_ctx); + if (status != QDF_STATUS_SUCCESS) + p2p_err("Failed to remove tx ctx from tx wait for roc queue"); + p2p_send_tx_conf(curr_tx_ctx, false); + qdf_mem_free(curr_tx_ctx->buf); + qdf_mem_free(curr_tx_ctx); + } + + p2p_debug("clean up tx queue wait for ack, size:%d", + qdf_list_size(&p2p_soc_obj->tx_q_ack)); + list_for_each_safe(pos, tmp, &p2p_soc_obj->tx_q_ack.anchor) { + curr_tx_ctx = list_entry(pos, + struct tx_action_context, node); + status = qdf_list_remove_node(&p2p_soc_obj->tx_q_ack, + (qdf_list_node_t *)curr_tx_ctx); + if (status != QDF_STATUS_SUCCESS) + p2p_err("Failed to remove tx ctx from tx wait for ack queue"); + 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); + } + + return status; +} + 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; + uint64_t rc = 1; + 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:%p, tx_ctx:%p, vdev_id:%d, scan_id:%d, roc_cookie:%llx, chan:%d, buf:%p, 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); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("unsupport frame"); + status = QDF_STATUS_E_INVAL; + goto fail; + } + + status = p2p_vdev_check_valid(tx_ctx); + if (status != QDF_STATUS_SUCCESS) { + p2p_err("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_CANCEL_IN_PROG) { + /* wait for roc canceling done */ + rc = qdf_wait_single_event( + &p2p_soc_obj->cancel_roc_done, + msecs_to_jiffies(P2P_WAIT_CANCEL_ROC)); + if (!rc) + p2p_err("Timeout occurred while waiting for RoC cancellation"); + } else { + p2p_adjust_tx_wait(tx_ctx); + status = p2p_restart_roc_timer(curr_roc_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; + } + } + + 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_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 cancel_roc_context cancel_roc; + + 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_err("Failed to find tx ctx by cookie, cookie %llx", + cancel_tx->cookie); + 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 p2p_soc_priv_obj *p2p_soc_obj; + struct p2p_start_param *start_param; + + p2p_soc_obj = tx_cnf_event->p2p_soc_obj; + tx_cnf = tx_cnf_event->tx_cnf; + + if (!p2p_soc_obj || !(p2p_soc_obj->start_param)) { + p2p_err("Invalid p2p soc object or start parameters"); + qdf_mem_free(tx_cnf); + return QDF_STATUS_E_INVAL; + } + + p2p_debug("soc:%p, vdev_id:%d, action_cookie:%llx, len:%d, status:%d, buf:%p", + p2p_soc_obj->soc, tx_cnf->vdev_id, + tx_cnf->action_cookie, tx_cnf->buf_len, + tx_cnf->status, tx_cnf->buf); + + /* disable tx timer */ + p2p_disable_tx_timer(tx_cnf_event->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_cnf_event->tx_ctx); + qdf_mem_free(tx_cnf); + 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; + + 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:%p, frame_len:%d, rx_chan:%d, vdev_id:%d, frm_type:%d, rx_rssi:%d, buf:%p", + 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); + + 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/core/src/wlan_p2p_off_chan_tx.h b/core/src/wlan_p2p_off_chan_tx.h index 6b5af43007cd..ea7cf4966dc1 100644 --- a/core/src/wlan_p2p_off_chan_tx.h +++ b/core/src/wlan_p2p_off_chan_tx.h @@ -26,18 +26,122 @@ #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 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) + 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_RSP: presence response frame + * @P2P_ACTION_NOT_SUPPORT: not support action frame type + */ +enum p2p_action_type { + 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 - * @action_cookie: Action cookie + * @roc_cookie: Cookie for remain on channel request * @chan: Chan for which this tx has been requested * @buf: tx buffer * @buf_len: Length of tx buffer @@ -46,13 +150,14 @@ struct p2p_rx_mgmt_event; * @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 action_cookie; + uint64_t roc_cookie; uint8_t chan; uint8_t *buf; int buf_len; @@ -61,8 +166,43 @@ struct tx_action_context { bool no_ack; uint32_t duration; qdf_mc_timer_t tx_timer; + struct p2p_frame_info frame_info; }; +/** + * 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_queue() - cleanup tx queue + * @p2p_soc_obj: p2p soc private object + * + * This function cleanup wait for roc queue and wait for ack queue. + * + * Return: QDF_STATUS_SUCCESS - in case of success + */ +QDF_STATUS p2p_cleanup_tx_queue(struct p2p_soc_priv_obj *p2p_soc_obj); + /** * p2p_process_mgmt_tx() - Process mgmt frame tx request * @tx_ctx: tx context @@ -90,7 +230,7 @@ QDF_STATUS p2p_process_mgmt_tx_cancel( * @tx_cnf_event: tx confirmation event information * * This function mgmt frame tx confirmation. It will deliver this - * event to HDD + * event to up layer * * Return: QDF_STATUS_SUCCESS - in case of success */ @@ -102,7 +242,7 @@ QDF_STATUS p2p_process_mgmt_tx_ack_cnf( * @rx_mgmt_event: rx mgmt frame event information * * This function mgmt frame rx mgmt frame event. It will deliver this - * event to HDD + * event to up layer * * Return: QDF_STATUS_SUCCESS - in case of success */ diff --git a/core/src/wlan_p2p_roc.c b/core/src/wlan_p2p_roc.c index fd588ebd5787..9c32eb61172e 100644 --- a/core/src/wlan_p2p_roc.c +++ b/core/src/wlan_p2p_roc.c @@ -419,6 +419,7 @@ static QDF_STATUS p2p_process_ready_on_channel_evt( p2p_debug("roc for off chan tx, ready to send frame"); cookie = (uintptr_t)roc_ctx; /* ready to tx frame */ + p2p_ready_to_tx_frame(p2p_soc_obj, cookie); } return status; diff --git a/dispatcher/inc/wlan_p2p_public_struct.h b/dispatcher/inc/wlan_p2p_public_struct.h index c17d5a940156..febd25f75f24 100644 --- a/dispatcher/inc/wlan_p2p_public_struct.h +++ b/dispatcher/inc/wlan_p2p_public_struct.h @@ -111,6 +111,7 @@ struct p2p_tx_cnf { * @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 { @@ -120,6 +121,7 @@ struct p2p_mgmt_tx { uint32_t len; uint32_t no_cck; uint32_t dont_wait_for_ack; + uint32_t off_chan; const uint8_t *buf; }; diff --git a/dispatcher/src/wlan_p2p_tgt_api.c b/dispatcher/src/wlan_p2p_tgt_api.c index d07c56c973e8..96fcf15bbf34 100644 --- a/dispatcher/src/wlan_p2p_tgt_api.c +++ b/dispatcher/src/wlan_p2p_tgt_api.c @@ -23,10 +23,19 @@ #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) @@ -103,12 +112,59 @@ void tgt_p2p_scan_event_cb(struct wlan_objmgr_vdev *vdev, QDF_STATUS tgt_p2p_mgmt_download_comp_cb(void *context, qdf_nbuf_t buf, bool free) { + p2p_debug("conext:%p, buf:%p, 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_cnf *tx_cnf; + struct p2p_tx_conf_event *tx_conf_event; + struct p2p_soc_priv_obj *p2p_soc_obj; + struct tx_action_context *tx_ctx; + struct scheduler_msg msg; + + p2p_debug("context:%p, buf:%p, status:%d, tx complete params:%p", + context, buf, status, tx_compl_params); + + if (!context) { + p2p_err("invalid context"); + return QDF_STATUS_E_INVAL; + } + tx_ctx = (struct tx_action_context *)context; + p2p_soc_obj = tx_ctx->p2p_soc_obj; + + tx_conf_event = qdf_mem_malloc(sizeof(*tx_conf_event)); + if (tx_conf_event == NULL) { + p2p_err("Failed to allocate tx cnf event"); + return QDF_STATUS_E_NOMEM; + } + + tx_cnf = qdf_mem_malloc(sizeof(*tx_cnf)); + if (tx_cnf == NULL) { + p2p_err("Failed to allocate tx cnf"); + return QDF_STATUS_E_NOMEM; + } + + qdf_nbuf_free(buf); + tx_cnf->vdev_id = tx_ctx->vdev_id; + tx_cnf->action_cookie = (uintptr_t)tx_ctx; + tx_cnf->buf = tx_ctx->buf; + tx_cnf->buf_len = tx_ctx->buf_len; + tx_cnf->status = status; + tx_conf_event->p2p_soc_obj = p2p_soc_obj; + tx_conf_event->tx_cnf = tx_cnf; + tx_conf_event->tx_ctx = tx_ctx; + msg.type = P2P_EVENT_MGMT_TX_ACK_CNF; + msg.bodyptr = tx_conf_event; + msg.callback = p2p_process_evt; + scheduler_post_msg(QDF_MODULE_ID_TARGET_IF, &msg); + return QDF_STATUS_SUCCESS; } @@ -117,6 +173,83 @@ QDF_STATUS tgt_p2p_mgmt_frame_rx_cb(struct wlan_objmgr_psoc *psoc, 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; + struct wlan_frame_hdr *wh; + struct wlan_objmgr_vdev *vdev; + struct p2p_roc_context *roc_ctx; + uint32_t vdev_id; + uint8_t *pdata; + + p2p_debug("psoc:%p, peer:%p, 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 == NULL) { + p2p_err("p2p ctx is NULL, drop this frame"); + return QDF_STATUS_E_FAILURE; + } + + if (peer == NULL) { + roc_ctx = p2p_find_current_roc_ctx(p2p_soc_obj); + if (roc_ctx == NULL) { + p2p_err("current roc ctx is null, can't get vdev id"); + return QDF_STATUS_E_FAILURE; + } else { + vdev_id = roc_ctx->vdev_id; + } + } else { + wlan_peer_obj_lock(peer); + vdev = wlan_peer_get_vdev(peer); + wlan_peer_obj_unlock(peer); + if (vdev == NULL) { + p2p_err("vdev is NULL in peer, drop this frame"); + return QDF_STATUS_E_FAILURE; + } + wlan_vdev_obj_lock(vdev); + vdev_id = wlan_vdev_get_id(vdev); + wlan_vdev_obj_unlock(vdev); + } + + rx_mgmt_event = qdf_mem_malloc(sizeof(*rx_mgmt_event)); + if (rx_mgmt_event == NULL) { + p2p_err("Failed to allocate rx mgmt event"); + return QDF_STATUS_E_NOMEM; + } + + rx_mgmt = qdf_mem_malloc(sizeof(*rx_mgmt) + + mgmt_rx_params->buf_len); + if (rx_mgmt == NULL) { + p2p_err("Failed to allocate rx mgmt frame"); + return QDF_STATUS_E_NOMEM; + } + + wh = (struct wlan_frame_hdr *)qdf_nbuf_data(buf); + 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 = (wh)->i_fc[0] & IEEE80211_FC0_TYPE_MASK; + 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; + scheduler_post_msg(QDF_MODULE_ID_TARGET_IF, &msg); + + qdf_nbuf_free(buf); + return QDF_STATUS_SUCCESS; } diff --git a/dispatcher/src/wlan_p2p_ucfg_api.c b/dispatcher/src/wlan_p2p_ucfg_api.c index 88b57ce7cc93..cc742fc2d39f 100644 --- a/dispatcher/src/wlan_p2p_ucfg_api.c +++ b/dispatcher/src/wlan_p2p_ucfg_api.c @@ -27,6 +27,7 @@ #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" static inline struct wlan_lmac_if_p2p_tx_ops * ucfg_p2p_psoc_get_tx_ops(struct wlan_objmgr_psoc *psoc) @@ -150,12 +151,96 @@ QDF_STATUS ucfg_p2p_roc_cancel_req(struct wlan_objmgr_psoc *soc, QDF_STATUS ucfg_p2p_mgmt_tx(struct wlan_objmgr_psoc *soc, struct p2p_mgmt_tx *mgmt_frm, uint64_t *cookie) { + struct scheduler_msg msg; + struct p2p_soc_priv_obj *p2p_soc_obj; + struct tx_action_context *tx_action; + + p2p_debug("soc:%p, 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 == NULL) { + 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 == NULL) { + p2p_err("P2P soc context is NULL"); + return QDF_STATUS_E_FAILURE; + } + + tx_action = qdf_mem_malloc(sizeof(*tx_action)); + if (tx_action == NULL) { + 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) + *cookie = 0; + else + *cookie = (uintptr_t)tx_action; + + 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 == NULL) { + 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); + msg.type = P2P_MGMT_TX; + msg.bodyptr = tx_action; + msg.callback = p2p_process_cmd; + scheduler_post_msg(QDF_MODULE_ID_OS_IF, &msg); + return QDF_STATUS_SUCCESS; } QDF_STATUS ucfg_p2p_mgmt_tx_cancel(struct wlan_objmgr_psoc *soc, uint64_t cookie) { + struct scheduler_msg msg; + struct p2p_soc_priv_obj *p2p_soc_obj; + struct cancel_roc_context *cancel_tx; + + p2p_debug("soc:%p, cookie:0x%llx", soc, cookie); + + if (soc == NULL) { + 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 == NULL) { + p2p_err("p2p soc context is NULL"); + return QDF_STATUS_E_FAILURE; + } + + cancel_tx = qdf_mem_malloc(sizeof(*cancel_tx)); + if (cancel_tx == NULL) { + p2p_err("Failed to allocate cancel p2p roc"); + return QDF_STATUS_E_NOMEM; + } + + cancel_tx->p2p_soc_obj = p2p_soc_obj; + cancel_tx->cookie = cookie; + msg.type = P2P_MGMT_TX_CANCEL; + msg.bodyptr = cancel_tx; + msg.callback = p2p_process_cmd; + scheduler_post_msg(QDF_MODULE_ID_OS_IF, &msg); + return QDF_STATUS_SUCCESS; }