/*
 * 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_scan_result_utils.cc contains the utility functions
 * LIM uses for maintaining and accessing scan results on STA.
 * Author:        Chandra Modumudi
 * Date:          02/13/02
 * History:-
 * Date           Modified by    Modification Information
 * --------------------------------------------------------------------
 */

#include "lim_types.h"
#include "lim_utils.h"
#include "lim_ser_des_utils.h"
#include "lim_api.h"
#include "lim_ft_defs.h"
#include "lim_session.h"
#include "rrm_api.h"
#include "cds_utils.h"


/**
 * lim_collect_bss_description()
 *
 ***FUNCTION:
 * This function is called during scan upon receiving
 * Beacon/Probe Response frame to check if the received
 * frame matches scan criteria, collect BSS description
 * and add it to cached scan results.
 *
 ***LOGIC:
 *
 ***ASSUMPTIONS:
 * NA
 *
 ***NOTE:
 * NA
 *
 * @param  pMac - Pointer to Global MAC structure
 * @param  pBPR - Pointer to parsed Beacon/Probe Response structure
 * @param  pRxPacketInfo  - Pointer to Received frame's BD
 * @param  fScanning - flag to indicate if it is during scan.
 * ---------------------------------------------
 *
 * @return None
 */
void
lim_collect_bss_description(tpAniSirGlobal pMac,
			    tSirBssDescription *pBssDescr,
			    tpSirProbeRespBeacon pBPR,
			    uint8_t *pRxPacketInfo, uint8_t fScanning)
{
	uint8_t *pBody;
	uint32_t ieLen = 0;
	tpSirMacMgmtHdr pHdr;
	uint8_t channelNum;
	uint8_t rxChannel;
	uint8_t rfBand = 0;

	pHdr = WMA_GET_RX_MAC_HEADER(pRxPacketInfo);

	if (SIR_MAC_B_PR_SSID_OFFSET > WMA_GET_RX_PAYLOAD_LEN(pRxPacketInfo)) {
		QDF_ASSERT(WMA_GET_RX_PAYLOAD_LEN(pRxPacketInfo) >=
			   SIR_MAC_B_PR_SSID_OFFSET);
		return;
	}
	ieLen =
		WMA_GET_RX_PAYLOAD_LEN(pRxPacketInfo) - SIR_MAC_B_PR_SSID_OFFSET;
	rxChannel = WMA_GET_RX_CH(pRxPacketInfo);
	pBody = WMA_GET_RX_MPDU_DATA(pRxPacketInfo);
	rfBand = WMA_GET_RX_RFBAND(pRxPacketInfo);

	/**
	 * Length of BSS desription is without length of
	 * length itself and length of pointer that holds ieFields.
	 *
	 * tSirBssDescription
	 * +--------+---------------------------------+---------------+
	 * | length | other fields                    | pointer to IEs|
	 * +--------+---------------------------------+---------------+
	 *                                            ^
	 *                                            ieFields
	 */
	pBssDescr->length = (uint16_t)(offsetof(tSirBssDescription, ieFields[0])
					- sizeof(pBssDescr->length) + ieLen);

	/* Copy BSS Id */
	qdf_mem_copy((uint8_t *) &pBssDescr->bssId,
		     (uint8_t *) pHdr->bssId, sizeof(tSirMacAddr));

	/* Copy Timestamp, Beacon Interval and Capability Info */
	pBssDescr->scansystimensec = qdf_get_bootbased_boottime_ns();

	pBssDescr->timeStamp[0] = pBPR->timeStamp[0];
	pBssDescr->timeStamp[1] = pBPR->timeStamp[1];
	pBssDescr->beaconInterval = pBPR->beaconInterval;
	pBssDescr->capabilityInfo =
		lim_get_u16((uint8_t *) &pBPR->capabilityInfo);

	if (!pBssDescr->beaconInterval) {
		pe_warn("Beacon Interval is ZERO, making it to default 100 "
			   MAC_ADDRESS_STR, MAC_ADDR_ARRAY(pHdr->bssId));
		pBssDescr->beaconInterval = 100;
	}
	/*
	 * There is a narrow window after Channel Switch msg is sent to HAL and before the AGC is shut
	 * down and beacons/Probe Rsps can trickle in and we may report the incorrect channel in 5Ghz
	 * band, so not relying on the 'last Scanned Channel' stored in LIM.
	 * Instead use the value returned by RXP in BD. This the the same value which HAL programs into
	 * RXP before every channel switch.
	 * Right now there is a problem in 5Ghz, where we are receiving beacons from a channel different from
	 * the currently scanned channel. so incorrect channel is reported to CSR and association does not happen.
	 * So for now we keep on looking for the channel info in the beacon (DSParamSet IE OR HT Info IE), and only if it
	 * is not present in the beacon, we go for the channel info present in RXP.
	 * This fix will work for 5Ghz 11n devices, but for 11a devices, we have to rely on RXP routing flag to get the correct channel.
	 * So The problem of incorrect channel reporting in 5Ghz will still remain for 11a devices.
	 */
	pBssDescr->channelId = lim_get_channel_from_beacon(pMac, pBPR);

	if (pBssDescr->channelId == 0) {
		/* If the channel Id is not retrieved from Beacon, extract the channel from BD */
		if (!rxChannel) {
			rxChannel = pMac->lim.gLimCurrentScanChannelId;
		}
		pBssDescr->channelId = rxChannel;
	}

	pBssDescr->channelIdSelf = pBssDescr->channelId;
	pBssDescr->rx_channel = rxChannel;

	/* set the network type in bss description */
	channelNum = pBssDescr->channelId;
	pBssDescr->nwType =
		lim_get_nw_type(pMac, channelNum, SIR_MAC_MGMT_FRAME, pBPR);

	/* Copy RSSI & SINR from BD */
	pBssDescr->rssi = (int8_t) WMA_GET_RX_RSSI_NORMALIZED(pRxPacketInfo);
	pBssDescr->rssi_raw = (int8_t) WMA_GET_RX_RSSI_RAW(pRxPacketInfo);

	/* SINR no longer reported by HW */
	pBssDescr->sinr = 0;
	pe_debug(MAC_ADDRESS_STR " rssi: normalized: %d, absolute: %d",
		MAC_ADDR_ARRAY(pHdr->bssId), pBssDescr->rssi,
		pBssDescr->rssi_raw);

	pBssDescr->received_time = (uint64_t)qdf_mc_timer_get_system_time();
	pBssDescr->tsf_delta = WMA_GET_RX_TSF_DELTA(pRxPacketInfo);
	pBssDescr->seq_ctrl = pHdr->seqControl;

	pe_debug("BSSID: "MAC_ADDRESS_STR " tsf_delta: %u ReceivedTime: %llu ssid: %s",
		  MAC_ADDR_ARRAY(pHdr->bssId), pBssDescr->tsf_delta,
		  pBssDescr->received_time,
		  ((pBPR->ssidPresent) ? (char *)pBPR->ssId.ssId : ""));

	pe_debug("Seq Ctrl: Frag Num: %d Seq Num: LO: %02x HI: %02x",
		pBssDescr->seq_ctrl.fragNum, pBssDescr->seq_ctrl.seqNumLo,
		pBssDescr->seq_ctrl.seqNumHi);

	if (fScanning) {
		rrm_get_start_tsf(pMac, pBssDescr->startTSF);
		pBssDescr->parentTSF = WMA_GET_RX_TIMESTAMP(pRxPacketInfo);
	}

	/* MobilityDomain */
	pBssDescr->mdie[0] = 0;
	pBssDescr->mdie[1] = 0;
	pBssDescr->mdie[2] = 0;
	pBssDescr->mdiePresent = false;
	/* If mdie is present in the probe resp we */
	/* fill it in the bss description */
	if (pBPR->mdiePresent) {
		pBssDescr->mdiePresent = true;
		pBssDescr->mdie[0] = pBPR->mdie[0];
		pBssDescr->mdie[1] = pBPR->mdie[1];
		pBssDescr->mdie[2] = pBPR->mdie[2];
	}

#ifdef FEATURE_WLAN_ESE
	pBssDescr->QBSSLoad_present = false;
	pBssDescr->QBSSLoad_avail = 0;
	if (pBPR->QBSSLoad.present) {
		pBssDescr->QBSSLoad_present = true;
		pBssDescr->QBSSLoad_avail = pBPR->QBSSLoad.avail;
	}
#endif
	/* Copy IE fields */
	qdf_mem_copy((uint8_t *) &pBssDescr->ieFields,
		     pBody + SIR_MAC_B_PR_SSID_OFFSET, ieLen);

	/*set channel number in beacon in case it is not present */
	pBPR->channelNumber = pBssDescr->channelId;

	pe_debug("Collected BSS Description for Channel: %1d length: %u IE Fields: %u",
		pBssDescr->channelId, pBssDescr->length, ieLen);
	pMac->lim.beacon_probe_rsp_cnt_per_scan++;

	return;
} /*** end lim_collect_bss_description() ***/
