/*
 * Copyright (c) 2011-2017 The Linux Foundation. All rights reserved.
 *
 * Previously licensed under the ISC license by Qualcomm Atheros, Inc.
 *
 *
 * 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.
 */

/*
 * This file was originally distributed by Qualcomm Atheros, Inc.
 * under proprietary terms before Copyright ownership was assigned
 * to the Linux Foundation.
 */

/*
 *
 * This file lim_process_probe_rsp_frame.cc contains the code
 * for processing Probe Response Frame.
 * Author:        Chandra Modumudi
 * Date:          03/01/02
 * History:-
 * Date           Modified by    Modification Information
 * --------------------------------------------------------------------
 *
 */

#include "wni_api.h"
#include "wni_cfg.h"
#include "ani_global.h"
#include "sch_api.h"
#include "utils_api.h"
#include "lim_api.h"
#include "lim_types.h"
#include "lim_utils.h"
#include "lim_assoc_utils.h"
#include "lim_prop_exts_utils.h"
#include "lim_ser_des_utils.h"
#include "lim_send_messages.h"

#include "parser_api.h"

/**
 * lim_validate_ie_information_in_probe_rsp_frame () - validates ie
 * information in probe response.
 * @mac_ctx: mac context
 * @pRxPacketInfo: Rx packet info
 *
 * Return: 0 on success, one on failure
 */
static tSirRetStatus
lim_validate_ie_information_in_probe_rsp_frame(tpAniSirGlobal mac_ctx,
				uint8_t *pRxPacketInfo)
{
	tSirRetStatus status = eSIR_SUCCESS;
	uint8_t *pframe;
	uint32_t nframe;
	uint32_t missing_rsn_bytes;

	/*
	 * Validate a Probe response frame for malformed frame.
	 * If the frame is malformed then do not consider as it
	 * may cause problem fetching wrong IE values
	 */

	if (WMA_GET_RX_PAYLOAD_LEN(pRxPacketInfo) <
		(SIR_MAC_B_PR_SSID_OFFSET + SIR_MAC_MIN_IE_LEN))
		return eSIR_FAILURE;

	pframe = WMA_GET_RX_MPDU_DATA(pRxPacketInfo);
	nframe = WMA_GET_RX_PAYLOAD_LEN(pRxPacketInfo);
	missing_rsn_bytes = 0;

	status = sir_validate_and_rectify_ies(mac_ctx,
			pframe, nframe, &missing_rsn_bytes);

	if (status == eSIR_SUCCESS)
		WMA_GET_RX_MPDU_LEN(pRxPacketInfo) += missing_rsn_bytes;

	return status;
}

/**
 * lim_process_probe_rsp_frame() - processes received Probe Response frame
 * @mac_ctx: Pointer to Global MAC structure
 * @rx_Packet_info: A pointer to Buffer descriptor + associated PDUs
 * @session_entry: Handle to the session.
 *
 * This function processes received Probe Response frame.
 * Frames with out-of-order IEs are dropped.
 * In case of IBSS, join 'success' makes MLM state machine
 * transition into 'BSS started' state. This may have to change
 * depending on supporting what kinda Authentication in IBSS.
 *
 * Return: None
 */
void
lim_process_probe_rsp_frame(tpAniSirGlobal mac_ctx, uint8_t *rx_Packet_info,
			    tpPESession session_entry)
{
	uint8_t *body;
	uint32_t frame_len = 0;
	tSirMacAddr current_bssid;
	tpSirMacMgmtHdr header;
	tSirProbeRespBeacon *probe_rsp;
	uint8_t qos_enabled = false;
	uint8_t wme_enabled = false;

	if (!session_entry) {
		pe_err("session_entry is NULL");
		return;
	}
	pe_debug("SessionId: %d ProbeRsp Frame is received",
		session_entry->peSessionId);

	probe_rsp = qdf_mem_malloc(sizeof(tSirProbeRespBeacon));
	if (NULL == probe_rsp) {
		pe_err("Unable to allocate memory");
		return;
	}

	probe_rsp->ssId.length = 0;
	probe_rsp->wpa.length = 0;

	header = WMA_GET_RX_MAC_HEADER(rx_Packet_info);

	pe_debug("Rx Probe Response with length = %d from "MAC_ADDRESS_STR,
		WMA_GET_RX_MPDU_LEN(rx_Packet_info),
		MAC_ADDR_ARRAY(header->sa));

	/* Validate IE information before processing Probe Response Frame */
	if (lim_validate_ie_information_in_probe_rsp_frame(mac_ctx,
				rx_Packet_info) !=
		eSIR_SUCCESS) {
		pe_err("Parse error ProbeResponse, length=%d", frame_len);
		qdf_mem_free(probe_rsp);
		return;
	}

	frame_len = WMA_GET_RX_PAYLOAD_LEN(rx_Packet_info);
	QDF_TRACE(QDF_MODULE_ID_PE, QDF_TRACE_LEVEL_DEBUG,
		FL("Probe Resp Frame Received: BSSID "
		MAC_ADDRESS_STR " (RSSI %d)"),
		MAC_ADDR_ARRAY(header->bssId),
		(uint) abs((int8_t)WMA_GET_RX_RSSI_NORMALIZED(rx_Packet_info)));
	/* Get pointer to Probe Response frame body */
	body = WMA_GET_RX_MPDU_DATA(rx_Packet_info);
		/* Enforce Mandatory IEs */
	if ((sir_convert_probe_frame2_struct(mac_ctx,
		body, frame_len, probe_rsp) == eSIR_FAILURE) ||
		!probe_rsp->ssidPresent) {
		pe_err("Parse error ProbeResponse, length=%d", frame_len);
		qdf_mem_free(probe_rsp);
		return;
	}

	if (session_entry->limMlmState ==
			eLIM_MLM_WT_JOIN_BEACON_STATE) {
		/*
		 * Either Beacon/probe response is required.
		 * Hence store it in same buffer.
		 */
		if (session_entry->beacon != NULL) {
			qdf_mem_free(session_entry->beacon);
			session_entry->beacon = NULL;
			session_entry->bcnLen = 0;
		}
		session_entry->bcnLen =
			WMA_GET_RX_PAYLOAD_LEN(rx_Packet_info);
			session_entry->beacon =
			qdf_mem_malloc(session_entry->bcnLen);
		if (NULL == session_entry->beacon) {
			pe_err("No Memory to store beacon");
		} else {
			/*
			 * Store the Beacon/ProbeRsp.
			 * This is sent to csr/hdd in join cnf response.
			 */
			qdf_mem_copy(session_entry->beacon,
				     WMA_GET_RX_MPDU_DATA
					     (rx_Packet_info),
				     session_entry->bcnLen);
		}
			/* STA in WT_JOIN_BEACON_STATE */
		lim_check_and_announce_join_success(mac_ctx, probe_rsp,
						header,
						session_entry);
	} else if (session_entry->limMlmState ==
		   eLIM_MLM_LINK_ESTABLISHED_STATE) {
		tpDphHashNode sta_ds = NULL;
		/*
		 * Check if this Probe Response is for
		 * our Probe Request sent upon reaching
		 * heart beat threshold
		 */
		sir_copy_mac_addr(current_bssid, session_entry->bssId);
		if (qdf_mem_cmp(current_bssid, header->bssId,
				sizeof(tSirMacAddr))) {
			qdf_mem_free(probe_rsp);
			return;
		}
		if (!LIM_IS_CONNECTION_ACTIVE(session_entry)) {
			pe_warn("Recved Probe Resp from AP,AP-alive");
			if (probe_rsp->HTInfo.present)
				lim_received_hb_handler(mac_ctx,
					probe_rsp->HTInfo.primaryChannel,
					session_entry);
			else
				lim_received_hb_handler(mac_ctx,
					(uint8_t)probe_rsp->channelNumber,
					session_entry);
		}
		if (LIM_IS_STA_ROLE(session_entry) &&
				!wma_is_csa_offload_enabled()) {
			if (probe_rsp->channelSwitchPresent) {
				/*
				 * on receiving channel switch announcement
				 * from AP, delete all TDLS peers before
				 * leaving BSS and proceed for channel switch
				 */
				lim_delete_tdls_peers(mac_ctx, session_entry);

				lim_update_channel_switch(mac_ctx,
					probe_rsp,
					session_entry);
			} else if (session_entry->gLimSpecMgmt.dot11hChanSwState
				== eLIM_11H_CHANSW_RUNNING) {
				lim_cancel_dot11h_channel_switch(
					mac_ctx, session_entry);
			}
		}
		/*
		 * Now Process EDCA Parameters, if EDCAParamSet
		 * count is different.
		 * -- While processing beacons in link established
		 * state if it is determined that
		 * QoS Info IE has a different count for EDCA Params,
		 * and EDCA IE is not present in beacon,
		 * then probe req is sent out to get the EDCA params.
		 */
		sta_ds = dph_get_hash_entry(mac_ctx,
				DPH_STA_HASH_INDEX_PEER,
				&session_entry->dph.dphHashTable);
		limGetQosMode(session_entry, &qos_enabled);
		limGetWmeMode(session_entry, &wme_enabled);
		pe_debug("wmeEdcaPresent: %d wme_enabled: %d"
			"edcaPresent: %d, qos_enabled: %d"
			"edcaParams.qosInfo.count: %d"
			"schObject.gLimEdcaParamSetCount: %d",
			probe_rsp->wmeEdcaPresent, wme_enabled,
			probe_rsp->edcaPresent, qos_enabled,
			probe_rsp->edcaParams.qosInfo.count,
			session_entry->gLimEdcaParamSetCount);

		if (((probe_rsp->wmeEdcaPresent && wme_enabled) ||
		     (probe_rsp->edcaPresent && qos_enabled)) &&
		    (probe_rsp->edcaParams.qosInfo.count !=
		     session_entry->gLimEdcaParamSetCount)) {
			if (sch_beacon_edca_process(mac_ctx,
				&probe_rsp->edcaParams,
				session_entry) != eSIR_SUCCESS) {
				pe_err("EDCA param process error");
			} else if (sta_ds != NULL) {
				/*
				 * If needed, downgrade the
				 * EDCA parameters
				 */
				lim_set_active_edca_params(mac_ctx,
						session_entry->
						gLimEdcaParams,
						session_entry);
					lim_send_edca_params(mac_ctx,
					session_entry->
					gLimEdcaParamsActive,
					sta_ds->bssId);
			} else {
				pe_err("SelfEntry missing in Hash");
			}
		}
		if (session_entry->fWaitForProbeRsp == true) {
			pe_warn("Check probe resp for caps change");
			lim_detect_change_in_ap_capabilities(
				mac_ctx, probe_rsp, session_entry);
		}
	} else {
		if (LIM_IS_IBSS_ROLE(session_entry) &&
		    (session_entry->limMlmState ==
				eLIM_MLM_BSS_STARTED_STATE))
			lim_handle_ibss_coalescing(mac_ctx, probe_rsp,
					rx_Packet_info, session_entry);
	}
	qdf_mem_free(probe_rsp);

	/* Ignore Probe Response frame in all other states */
	return;
}
